1.想让一个类实现copy方法就要遵循NSCopying协议
如果想让类实现copy方法,就要遵循NSCopying协议,同时重写这个协议的唯一一个方法copyWithZone方法。
-(id)copyWithZone:(NSZone*)zone;
⚠️:其实我们并不是重写了copy方法,而是重写了copyWithZone方法
2.如果想让一个类有可变版本和不可变版本就要同时遵循NSCopying协议和NSMutableCopying协议
NSMutableCopying协议里只有一个方法:
-(id)mutableCopyWithZone:(NSZone*)zone;
3.可变版本和不可变版本
无论当前实例是否可变,若需获取其可变版本的拷贝都应该调用mutableCopy方法,如果要获得不可变版本的拷贝就应该调用copy方法。
[NSMutableArray copy]=>NSArray
[NSArray mutableCopy]=>NSMutableArray
但是这么做是不安全的,比如说一个类本来是可变的,然后copy之后变成了不可变的,这个时候再去调用以前的可变类的方法会crash。
4.深拷贝和浅拷贝
⚠️:这里有一个特别重要的误区,不是说调用copy方法就是浅拷贝,调用mutablecopy就是深拷贝,并没有专门定义深拷贝的协议,所以深拷贝方法的实现是需要自己去确定的。
浅拷贝:浅拷贝是对指针的拷贝,对一个对象进行浅拷贝,相当于拷贝了一个指向它的指针,相当于把对象的引用计数加1,指针指向的内存地址就是原对象的内存地址。
深拷贝:深拷贝是对值的拷贝,相当于拷贝出了一个完全新的对象,只是对象的值和原对象的值相等。
非容器对象:
NSString* string = @"laowang";
NSString* str1 = [string copy];
NSString* str2 = [string mutableCopy];
NSLog(@"string:%p",string);
NSLog(@"str1:%p",str1);//和string是一个内存地址
NSLog(@"str2:%p",str2);//和string是不同的内存地址
⚠️打印结果:
string:0x10265c060、str1:0x10265c060、str2:0x7f8b7ae96860
:对于不可变的非容器对象,copy是浅拷贝,mutableCopy是深拷贝
对于可变的非容器对象,无论是copy还是mutableCopy都是深拷贝,拷贝出来的都是新的对象。
NSMutableString* mtr = [NSMutableString stringWithFormat:@"laowang"];
NSString* mtr1 = [mtr copy];
NSString* mtr2 = [mtr mutableCopy];
NSMutableString* mtr3 = [mtr copy];这里这个mtr3虽然写的是可变字符串,但是调用可变字符串的方法appendString程序会崩溃!
NSMutableString* mtr4 = [mtr mutableCopy];
NSLog(@"mtr=%p",mtr);
NSLog(@"mtr1=%p",mtr1);
NSLog(@"mtr2=%p",mtr2);
NSLog(@"mtr3=%p",mtr3);
NSLog(@"mtr4=%p",mtr4);
结果:mtr=0x7faaf8d1e7c0、mtr1=0xa676e61776f616c7、mtr2=0x7faaf8d1e800
mtr3=0xa676e61776f616c7、mtr4=0x7faaf8d1e840
不可变容器对象:
NSArray* array = [NSArray arrayWithObjects:@"1",@"2",@"3", nil];
NSArray* array1 = [array copy];//浅拷贝
NSArray* array2 = [array mutableCopy];//深拷贝
NSLog(@"array = %p",array);
NSLog(@"array1 = %p",array1);
NSLog(@"array2 = %p",array2);
结论:array = 0x7f8073426bd0、array1 = 0x7f8073426bd0、array2 = 0x7f8073426c00
:copy对容器对象本身是浅拷贝,对容器对象内对象也是浅拷贝
mutableCopy 对容器对象本身是深拷贝,对容器对象内对象也是浅拷贝
可以用一个图说明:
可变容器对象的拷贝:
无论是copy还是mutableCopy对容器对象本身都是深拷贝,但是对于容器对象内的每个对象都是浅拷贝(指针拷贝)
所以说foundation框架里的这些集合类都没有实现真正意义上的深拷贝,就是既对集合本身深拷贝,也对集合内的每个对象深拷贝,要想实现这样的深拷贝需要我们自己去给类添加深拷贝的实现方法。
实现完全深拷贝:
- (instancetype)initWithArray:(NSArray *)array copyItems:(BOOL)flag;
NSMutableArray *marry1 = [[NSMutableArray alloc] init];
NSMutableString *mstr1 = [[NSMutableString alloc]initWithString:@"value1"];
NSMutableString *mstr2 = [[NSMutableString alloc]initWithString:@"value2"];
[marry1 addObject:mstr1];
[marry1 addObject:mstr2];
NSArray *marray2 = [[NSArray alloc] initWithArray:marry1 copyItems:YES];
marray2里的每一个元素的内存地址和marry1里的都不同,所以说容器内的每个元素也是深拷贝。