一个对象使用copy或mutableCopy方法可以创建对象的副本
1.copy
需要实现NSCopying协议
创建出来的是不可变副本,如NSString, NSArray, NSDictionary
(1)不可变对象调用copy (NSSring除外)
不会产生新的对象,而是返回对象本身,相当于retain,计数器+1
属于浅拷贝
1 NSArray *arr1 = [NSArray arrayWithObjects:@"张三", @"李四", nil]; 2 NSLog(@"arr1.addr: %p", arr1); // 0x1002052b0 3 NSLog(@"arr1.retainCount: %ld", arr1.retainCount); // 1 4 5 6 NSArray *arr2 = [arr1 copy]; 7 NSLog(@"arr1.addr: %p", arr1); // 0x1002052b0 8 NSLog(@"arr1.retainCount: %ld", arr1.retainCount); // 2 <-- 浅拷贝,相当于retain, retainCount+1 9 NSLog(@"arr2.addr: %p", arr2); //0x1002052b0 <-- 浅拷贝,依旧指向同一个对象 10 NSLog(@"arr2.retainCount: %ld", arr2.retainCount); // 2 <-- 浅拷贝,还是原来对象的retainCount
(2)可变对象调用copy (NSMutableString会拷贝出一个NSString”常量对象")
返回一个不可变对象,但是不是原来的对象,属于深拷贝
1 NSMutableArray *arr1 = [NSMutableArray array]; 2 [arr1 addObject:@"123"]; 3 [arr1 addObject:@"abc"]; 4 NSLog(@"arr1.addr: %p", arr1); // 0x100204840 5 NSLog(@"arr1.retainCount: %ld", arr1.retainCount); // 1 6 7 8 NSMutableArray *arr2 = [arr1 copy]; 9 NSLog(@"arr1.addr: %p", arr1); // 0x100204840 10 NSLog(@"arr1.retainCount: %ld", arr1.retainCount); // 1 <-- 深拷贝,原来的对象retainCount保持 11 NSLog(@"arr2.addr: %p", arr2); // 0x100300000 <-- 深拷贝,开辟了新的内存空间,拷贝出来的实际对象是NSArray 12 NSLog(@"arr2.retainCount: %ld", arr2.retainCount); // 1 <-- 深拷贝,新的对象retainCount独立
(3)重点: NSString的特殊性
由于使用了 @“” 进行初始化,数据存放在了常量池
因为NSString指向字符串常量,系统不会收回,也不会对其作引用计数,即使我们对NSString变量如何retain或release,retainCount都是-1 (无符号最大值)
1 NSString *str1 = [[NSString alloc] initWithString:@"abc"]; 2 NSLog(@"str1.addr: %p", str1); // 0x100001030 3 NSLog(@"str1.retainCount: %ld", str1.retainCount); // -1 <-- 字符串常量存在常量池,不使用堆内存 4 5 NSString *str2 = [str1 copy]; 6 NSLog(@"str1.addr: %p", str1); // 0x100001030 7 NSLog(@"str1.retainCount: %ld", str1.retainCount); // -1 <-- 无论是copy, retain, release操作都不会改变retianCount 8 NSLog(@"str2.addr: %p", str2); // 0x100001030 <-- 拷贝之后的变量依旧指向原来的常量 9 NSLog(@"str2.retainCount: %ld", str2.retainCount); // -1 <-- 原来常量的retainCount 10 11 [str1 retain]; 12 NSLog(@"after retain -> str1.retainCount: %ld", str1.retainCount); // -1 <-- 无论是copy, retain, release操作都不会改变retianCount 13 [str2 release]; 14 NSLog(@"after release -> str2.retainCount: %ld", str2.retainCount); // -1 <-- 无论是copy, retain, release操作都不会改变retianCount
要使一个NSString变量也有retainCount:
就要是指向另外一个”NSString”对象,而不是字符串常量
1 // 先创建出一个NSString对象,再用另一个指向 2 NSString *str1 = [NSString stringWithFormat:@"abc"]; 3 NSLog(@"%ld", str1.retainCount); // -1 4 NSString *str2 = [NSString stringWithString:str1]; 5 NSLog(@"%ld", str2.retainCount); // 1
(4)copy出来的是不可变对象,如NSMutableString调用copy创建出来的实际是NSString
1 // 这里不能使用 NSMutableString *mstr1 = @"abc", 或者在延迟赋值 mstr1 = @"abc"; 否则同样指向常量池 2 NSMutableString *mstr1 = [[NSMutableString alloc] initWithString:@"abc"]; 3 NSLog(@"mstr1.addr: %p", mstr1); // 0x100404990 4 NSLog(@"mstr1.retainCount: %ld", mstr1.retainCount); // 1 5 6 7 NSMutableString *mstr2 = [mstr1 copy]; 8 NSLog(@"mstr1.addr: %p", mstr1); // 0x100404990 9 NSLog(@"mstr1.retainCount: %ld", mstr1.retainCount); // 1 <-- 深拷贝,原来的对象retainCount保持 10 NSLog(@"mstr2.addr: %p", mstr2); // 0x63626135 <-- 深拷贝,开辟了新的内存空间,但是指向的实际是一个字符串常量 11 NSLog(@"mstr2.retainCount: %ld", mstr2.retainCount); // -1 <-- 因为其实拷贝出来的是一个字符串常量
2.mutableCopy
需要实现NSMutableCopying协议
创建的是可变副本,如NSMutableString, NSMutableArray, NSMutableDictionary
(1)不可变对象调用mutableCopy
产生一个新的对象,新旧对象的计数器独立,不会互相干扰,属于深拷贝
1 NSString *str1 = [[NSString alloc] initWithString:@"abc"]; 2 NSLog(@"str1.addr: %p", str1); // 0x100001030 3 NSLog(@"str1.retainCount: %ld", str1.retainCount); // -1 <-- 字符串常量存在常量池,不使用堆内存 4 5 NSMutableString *mstr2 = [str1 mutableCopy]; // mutableCopy 出来的是一个 NSMutableString 对象 6 NSLog(@"str1.addr: %p", str1); // 0x100001030 7 NSLog(@"str1.retainCount: %ld", str1.retainCount); // -1 <-- 对于字符串常量, 无论是copy, retain, release操作都不会改变retianCount 8 NSLog(@"mstr2.addr: %p", mstr2); // 0x100106460 <-- 深拷贝,开辟新的空间 9 NSLog(@"mstr2.retainCount: %ld", mstr2.retainCount); // 1 <-- 由于是NSMutableString对象,retainCount == 1
(2)可变对象调用mutableCopy
产生一个新的对象,新旧对象的计数器独立,不会互相干扰,属于深拷贝
1 NSMutableString *mstr1 = [[NSMutableString alloc] initWithString:@"abc"]; 2 NSLog(@"mstr1.addr: %p", mstr1); // 0x100402910 3 NSLog(@"mstr1.retainCount: %ld", mstr1.retainCount); // 1 4 5 NSMutableString *mstr2 = [mstr1 mutableCopy]; // mutableCopy 出来的是一个 NSMutableString 对象 6 NSLog(@"mstr1.addr: %p", mstr1); // 0x100402910 7 NSLog(@"mstr1.retainCount: %ld", mstr1.retainCount); // 1 8 NSLog(@"mstr2.addr: %p", mstr2); // 0x100300e90 <-- 深拷贝,开辟新的空间 9 NSLog(@"mstr2.retainCount: %ld", mstr2.retainCount); // 1
3.自定义类的copy
1 @interface Student : NSObject 2 3 //copy代表setter会release旧对象,copy新对象 4 @property (nonatomic, copy) NSString *name; 5 6 @end
传入外部NSMutableString对象到student的setter,当外部对象改变的时候,不会改变student中copy来的对象
如果使用retain或者assign,就会影响,因为指向的是同一个对象
==>传入NSMutableString时(成员变量为NSString),一般使用copy策略,其他使用retain
(1)实现<NSCopying>
(2)实现 - (id) copyWithZone:(NSZone *) zone
1 - (id)copyWithZone: (NSZone *) zone 2 { 3 Student *stu = [[Student allocWithZone:zone] init]; 4 stu.name = self.name; 5 return stu; 6 }