NSDictionary对象,添加内容,然后通过代码产生可变和不可变副本,这些复制是深复制还是浅复制?那么对于可变对象呢?为什么NSString定义属性时需要用copy呢?copy属性和copy对象有什么不同?
首先先说一下copy和retain的区别:
copy是创建一个新对象,是内容拷贝。retain是创建一个指针,引用对象计数加1。retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1。
对于深复制和浅复制我们来看下面的代码:
NSDictionary *dict = [NSDictionarydictionaryWithObjectsAndKeys:@"1",@"1",@"2",@"2",nil];
NSLog(@"原字典:%p",dict);
NSDictionary *dictRetain = dict.retain;
NSLog(@"计数器:%p",dictRetain);
NSDictionary *dictCopy = dict.copy;
NSLog(@"浅复制:%p",dictCopy);
NSMutableDictionary *dictMutableCopy = dict.mutableCopy;
NSLog(@"深复制:%p",dictMutableCopy);
打印结果:
计数器:0x7f7f89d21ec0
浅复制:0x7f7f89d21ec0
深复制:0x7f7f89c09680
也就是说,对于retain和copy产生的字典内存与原地址内存相同,也就是说copy产生的不可变版本是浅复制和retain一样拷贝的是指针。而mutableCopy深复制是拷贝的对象内容。
下面我们看一个可变对象的拷贝过程:
NSMutableString *string = [NSMutableStringstringWithString:@"name"];
NSLog(@"strP:%p",string);
NSString *stringCopy = [stringcopy];
NSLog(@"copy:%p",stringCopy);
NSMutableString *mStringCopy = [stringcopy];
NSLog(@"copy:%p",mStringCopy);
NSMutableString *stringMCopy = [stringmutableCopy];
NSLog(@"mCopy:%p",stringMCopy);
打印结果:
strP:0x7faea1f124d0
copy:0xa000000656d616e4
copy:0xa000000656d616e4
mCopy:0x7faea1e0df20
对于可变对象进行复制,结果和上面不一样了。
我们可以这样理解:如果对一不可变对象复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。
还有一个问题就是:我们知道,在@property属性里copy说的是复制了一个新的对象,引用计数不加1.那么这里属性copy则是使用了深复制。而调用copy方法则是做了一个浅复制。
这也就是为什么NSString属性变量要用copy关键字。如果把一个NSString赋值给NSMutableString属性时,如果使用了strong,则相当于浅复制。两个指针指向同一块内存,当NSMutableString改变后,NSString也就改变了。如果使用了copy,则是深复制。两个对象存在于不同的内存中,当NSMutableString改变时,不影响NSString.
另外,对于字符串来说,我们在使用的时候还要注意:
initWithFormat会创建新空间,initWithString不一定创建新空间,取决于参数。如果后面是常量字符串则不会创建新空间,如果是定义新字符串,则会创建新空间。下面是测试代码:
NSString *name = [[NSString alloc]initWithFormat:@"张三"];
NSString *name = [[NSStringalloc]initWithString:@"张三"];
NSLog(@"%ld",[nameretainCount]);
[name retain];
NSLog(@"%ld",[nameretainCount]);
[name copy];
NSLog(@"%ld",[nameretainCount]);
[name mutableCopy];
NSLog(@"%ld",[nameretainCount])
结果:如果是initWithString,则输入结果全为-1,如果是initWithFormat,结果分别是:1,2,3,4