1. copy概念
浅复制与深复制
copy即复制,有浅复制和深复制两种。浅复制即引用复制,只拷贝对象的引用,不分配新的内存空间,深复制即对象拷贝,会创建一个对象的副本,分配新的内存空间,与原对象独立。
深复制(深拷贝,内容拷贝,deep copy)
源对象和副本对象是不同的两个对象
源对象引用计数器不变, 副本对象计数器为1(因为是新产生的)
本质是:产生了新的对象
浅复制(浅拷贝,指针拷贝,shallow copy)
源对象和副本对象是同一个对象
源对象(副本对象)引用计数器 + 1, 相当于做一次retain操作
本质是:没有产生新的对象
只有源对象和副本对象都不可变时,才是浅复制,其它都是深复制
NSstring类的retainCount方法,在OS中和iOS中貌似是不同的,(实践)
copy是浅复制,只是复制引用。mutablecopy是深复制,产生了新的空间。
实践结论:
单独对于字符串来说,NSString进行copy时浅复制,新对象地址与原对象一样(想想也可以理解,因为是不可变的常量字符串,存在常量区,因此没有必要额外分配内存)。NSMutableString进行copy后,新对象地址和原对象地址不一样,因此可得出结论NSMutableString进行copy后是深复制,但是深复制后的内容是却是不可变的。
而NSString进行mutablecopy后是深复制,新对象与原对象地址不一样,原对象在常量区,mutablecopy出来的新对象在堆区。NSMutableString进行mutablecopy则是深复制,内容可变。
如何使用copy功能
一个对象可以调用copy或mutableCopy方法来创建一个副本对象
copy : 创建的是不可变副本(如NSString、NSArray、NSDictionary)
mutableCopy :创建的是可变副本(如NSMutableString、NSMutableArray、NSMutableDictionary)
使用copy功能的前提
copy : 需要遵守NSCopying协议,实现copyWithZone:方法
@protocol NSCopying
- (id)copyWithZone:(NSZone *)zone;
@end
mutableCopy : 需要遵守NSMutableCopying协议,实现mutableCopyWithZone:方法
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone;
@end
2. @property中的copy关键字
@property内存管理策略的选择
1.非ARC
1> copy : 只用于NSString\block
2> retain : 除NSString\block以外的OC对象
3> assign : 基本数据类型、枚举、结构体(非OC对象),当2个对象相互引用,一端用retain,一端用assign
2.ARC
1> copy : 只用于NSString\block
2> strong : 除NSString\block以外的OC对象
3> weak : 当2个对象相互引用,一端用strong,一端用weak
4> assgin : 基本数据类型、枚举、结构体(非OC对象)
示例代码:
Dog.h
@interface Dog : NSObject<NSCopying>
@property (nonatomic,copy) NSString *name;
@end
Dog.m
@implementation Dog
- (id)copyWithZone:(NSZone *)zone{
NSLog(@"Dog copying...");
Dog *d = [[Dog alloc]init];
d.name = _name;
return d;
}
- (void)dealloc{
// [_name dealloc];
NSLog(@"Dog dealloc...");
// [super dealloc];
}
@end
main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
Dog *d = [Dog new];
NSMutableString *name = [NSMutableString stringWithFormat:@"金毛"];
d.name = name;
// [name appendString:@"哈士奇"];
// NSLog(@"d.name = %@",d.name);
// NSLog(@"name:%p d.name:%p",name,d.name);
Dog *d2 = [d copy];
NSLog(@"d=%p d2=%p",d,d2);
NSLog(@"d=%@ d2=%@",d.name,d2.name);
// NSLog(@"d.retaincount = %lu d2.retainCount = %lu",d.retainCount,d2.retainCount);
// [d release];
// NSLog(@"%@ %lu",d2.name,d2.retainCount);
}
return 0;
}
知识点一
@property 中的retain,strong,weak,与copy的对比。
MRC模式下:
1.name属性为retain时
Dog.h 里面 @property (nonatomic,retain) NSString *name; name属性为retain时,
Dog *d = [Dog new];
NSString *name = @”hasky”;
d.name = name;
由于是retain,因此d.name = name 时进行的时浅复制,只是复制了引用,当name值变化后,d.name也会跟着变化。
2.name属性为copy时
Dog.h 里面 @property (nonatomic,copy) NSString *name; name属性为copy时,
Dog *d = [Dog new];
NSString *name = @”hasky”;
d.name = name;
由于是copy,因此d.name = name 时进行的是深复制,分配了新的内存空间,d.name与name完全独立。
ARC模式下:
1. name属性为strong,weak时
Dog.h 里面 @property (nonatomic,strong) NSString *name; name属性为strong或weak时,
Dog *d = [Dog new];
NSString *name = @”hasky”;
d.name = name;
由于是strong属性,因此d.name = name 时进行的是浅复制(复制引用),没有分配内存空间,d.name与name地址相同,name改变后d.name也会随之改变。
2.name属性为copy时
Dog.h 里面 @property (nonatomic,copy) NSString *name; name属性为copy时,
Dog *d = [Dog new];
NSString *name = @”hasky”;
d.name = name;
由于是copy属性,因此d.name = name 时进行的是深复制,分配新的内存空间,d.name与name地址不相同,name与d.name互相独立。
3. 自定义类实现copy
自定义对象的copy都是深复制。
1. 遵循协议,实现- (id)copyWithZone:(NSZone *)zone方法。
在该方法中应该分配新的空间,并将原对象的值赋值给新对象,并返回新的对象。
如果没有分配新的空间,则copy后的新对象与原对象地址一样,确认是浅复制(在MRC模式下),奇怪的时新旧对象的retainCount并没有加1(为了保持深复制的特征?)
而原对象release后,新对象依旧可以使用原对象的空间。
Dog *d = [Dog new];
Dog *d2 = [d copy];
NSLog(@"d=%p d2=%p",d,d2);
NSLog(@"%lu %lu",d.retainCount,d2.retainCount);
[d release];
NSLog(@"%@ %lu",d2.name,d2.retainCount);
在- (id)copyWithZone:(NSZone *)zone方法中实现了分配空间,并赋值后,则完全就是深拷贝了。