IOS - 深入理解 深拷貝 與 淺拷貝

OC 數組中的深拷貝與淺拷貝

淺拷貝:即指針拷貝,源對象和新對象指向的是同一個地址,也就是說淺拷貝要復出出來一個新的文件,但兩個文件的地址還是一個。淺拷貝的話是隻有不可變數組(如:NSArray,NSSet,NS字典)遇上copy,纔是淺拷貝,剩下的都是深拷貝。

Dog * dog1 = [Dog new];
// 這裏就是淺拷貝,即指針拷貝
Dog * dog2 = dog1;

深拷貝,自己定義的類一般需要遵循 NSCopying, NSMutableCopying 協議

Dog.h

@interface Dog : NSObject<NSCopying, NSMutableCopying>
@property (nonatomic) NSInteger age;
@end
Dog.m

@implementation Dog
- (id)copyWithZone:(NSZone *)zone
{
    Dog * dog = [[self class] allocWithZone:zone];
    dog.age = self.age;
    return dog;
}

- (id)mutableCopyWithZone:(NSZone *)zone
{
    Dog * dog = [[self class] allocWithZone:zone];
    dog.age = self.age;
    return dog;
}

- (NSString *) description {
    return [NSString stringWithFormat:@"dog age: %ld", self.age];
}

@end
main.m

Dog * dog1 = [Dog new];
dog1.age = 5;

NSLog(@"dog1: %p, %@", dog1, dog1);

// mutableCopy 返回的是可變對象,自然可以設置對象的屬性值
Dog * dog2 = [dog1 mutableCopy];
dog2.age = 10;

// copy 返回的對象也可以設置對象的屬性值
// 所以,從這裏來看,其實自定義實現的 copy 和 mutableCopy 功能是一致的,都是返回可變對象的
Dog * dog2 = [dog1 copy];
dog2.age = 10;

NSLog(@"dog2: %p, %@", dog2, dog2);


OC 可變數組中的深拷貝與淺拷貝

“數組中只是存儲了對象的地址,而非存儲了對象的本體。”

所以,對數組進行深拷貝之後,其中對象的地址確實被重新創建。

NSMutableArray * arr1 = [NSMutableArray new];

for (int i = 0; i < 3; i++) {
    Dog * dog = [Dog new];
    [arr1 addObject:dog];
}

NSLog(@"arr1: %p, %@", arr1, arr1);
NSMutableArray * arr2 = [arr1 mutableCopy];
NSLog(@"arr2: %p, %@", arr2, arr2);
運行結果:
2017-010-10 10:55:39.895 深拷貝與淺拷貝[901:303] arr1: 0x1001089c0, (
    "<Dog: 0x100108430>",
    "<Dog: 0x100109fc0>",
    "<Dog: 0x100109fd0>"
)
2017-10-10 10:55:39.917 深拷貝與淺拷貝[901:303] arr2: 0x100301ad0, (
    "<Dog: 0x100108430>",
    "<Dog: 0x100109fc0>",
    "<Dog: 0x100109fd0>"
)

因爲 “數組中只是存儲了對象的地址,而非存儲了對象的本體。”所以,Dog 對象的空間並沒有被複制。

因此,可變數組的 “深拷貝”並沒有將其中所有元素都複製,其中的對象元素只進行了淺複製!

解決方法:重新將所有對象都拷貝一份

NSMutableArray * arr2 = [NSMutableArray new];
for (int i = 0; i < arr1.count; i++) {
    Dog * newDog = [arr1[i] copy];
    [arr2 addObject:newDog];
}

NSLog(@"arr2: %p, %@", arr2, arr2);

OC中對於數組的深拷貝,不能想當然地認爲,已經將其中元素空間都拷貝了。實際上,其中的元素只是淺拷貝而已!

如果需要實現深拷貝的話,可以通過一個一個元素進行深拷貝,重新添加到可變數組中!


COPY 返回一個不可變對象的副本,MutalbeCopy返回一個可變對象的副本。

NSArray *array=[NSArray arrayWithObjects:@"one",@"two", nil];
NSMutableArray *array1=[array copy];
[array1 addObject:@"three"];  //error
NSMutableArray *array2=[array mutableCopy];
[array2 addObject:@"three"];  //right
// insert code here...
NSLog(@"Hello, World!");


比較與區別

複製對象的基本概念:複製一個對象爲副本,開闢一塊新的內存來存儲副本對象。

如果一個對象想具備複製的功能,必須實現協議和協議

NSObject自帶的常用的對象有:NSNumber、NSString、NSArray、NSDictionary、NSMutableArray、NSMutableDictionay、NSMutableString,copy產生的對象時不可變的,mutableCopy產生的對象時可變的

COPY和MutableCopy的區別

COPY 返回一個不可變對象的副本,MutalbeCopy返回一個可變對象的副本。


淺copy和深copy

淺複製盡複製對象本身,對象裏的屬性、包含的對象不做複製。

淺複製只是複製指針,並沒有創建新的內存空間。

深複製複製全部,包括對象的屬性和其他對象

Foundation框架支持複製的類,默認是淺複製


對象的自定義拷貝

對象擁有複製特性,必須實現NSCopying,NSMutableCopying協議,實現該協議的copyWithZone方法和mutableCopyWithZone方法

深拷貝和淺拷貝的區別就在於copyWithZone方法的實現,


copy、mutableCopy和retain之間的關係

在Foundation對象中,copy是一個不可變的對象時,作用相當於retain

當使用mutableCopy時,不管源對象是否可變,副本是可變的,並且實現真正意義上的copy

當我們使用copy一個可變對象時,副本對象是不可變的。


NSFoundation,當我們copy的時一個不可變對象時,默認的copy都是淺拷貝,相當於retain

當使用mutableCopy時,不管對象是否可變,都會實現深拷貝

retain相當於兩個對象指向同一個指針


總結:copy與mutableCopy:
copy:對於可變對象爲深拷貝,對於不可變對象是淺拷貝。返回對象是否可變與被複制的對象保持一致。
mutableCopy:始終是深拷貝。始終返回一個可變對象。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章