由NSString的copy和strong/retain引出o-c的copy机制 (二)
2014-03-11 15:20
369 查看
上篇分析了NSString的copy和retain的区别,并且引出对oc中的copy原理探究的欲望,参考了很多资料,这里做讨论。
1、实现了NSCopy/NSMutableCopying的框架类
我们都知道oc框架里面的例如NSString、NSArray等很多类的对象在需要复制的时候都可以直接调用[obj copy/mutablecopy]方法。调用copy方法时,会向NSCopying的协议方法copywithzone发消息的。作为根类的NSObject并没有实现NSCopying协议,所以他们都默认实现了NSCopying和NSMutableCopying协议,实现copywithzone和mutablecopywithzone方法。
我们自定义的对象要想调用copy/mutablecopy方法,也必须手动实现NSCopying协议,实现两个方法。如果没有实现,调用copy方法会抛出异常。
定义一个person类
使用:
-(id)copyWithZone:(NSZone *)zone
{
Person* copy = [[self class] allocWithZone:zone]; //分配新的内存空间 对于self本身来说,属于深拷贝了。
copy.name = [self.name copy]; //NSString的copy,对于name,属于浅拷贝,因为都属于不可变
copy.age = self.age;
copy.sons = [self.sons copy]; //同样浅拷贝
return self;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
Person* copy = [[self class] allocWithZone:zone];//分配新的内存空间
copy.name = [self.name mutableCopy]; //深拷贝 不可变拷贝可变
copy.age = self.age;
copy.sons = [self.sons mutableCopy]; //同样深拷贝
return copy;
}
@end
2.1使用copy:
打印地址:
输出内容:
我们看到,地址不同,copy的内存空间是新开辟的。这里要说一下,因为一般我们copy出来的Person对象,副本变动不希望影响到另一个。所以在copywithzone里面如上面那样操作,新开辟内存空间,逐个成员变量赋值;如果copy的对象不考虑互相影响的后果,则可以直接:
Person的name和sons都属于不可变,并且调用的是copy,所以对象p和copy的这两个成员的地址应该是一样的。 我们打印出name和sons的值和地址来看一下:
这里注意到,我专门打印出p.sons 和copy.sons的第一个元素地址来做对比,他们是一样的。NSArray是容器类型,里面的元素是在copy的时候是什么样的情况,后面详细讨论。
2.2使用mutablecopy
可以看到,copywithzone和mutablecopywithzone都创建了新的内存空间,不同之处在于对成员变量的处理上。
3、容器类对象的拷贝 参考:/article/4950670.html
对于类似NSArray、NSDictionary之类的容器元素,要想实现深拷贝,可以参考一下两种方法:
trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。举个例子,[[array objectAtIndex:0]appendstring:@”sd”]后其他的容器内对象并不会受影响。[[array objectAtIndex:1]和[[deepCopyArray
objectAtIndex:0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是apple的官方文档将其列为deep copy了,并添加了copy和mutablity的关系说明,故在此做一说明
1、实现了NSCopy/NSMutableCopying的框架类
我们都知道oc框架里面的例如NSString、NSArray等很多类的对象在需要复制的时候都可以直接调用[obj copy/mutablecopy]方法。调用copy方法时,会向NSCopying的协议方法copywithzone发消息的。作为根类的NSObject并没有实现NSCopying协议,所以他们都默认实现了NSCopying和NSMutableCopying协议,实现copywithzone和mutablecopywithzone方法。
@interface NSString : NSObject <NSCopying, NSMutableCopying, NSSecureCoding> @interface NSArray : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration> @interface NSDictionary : NSObject <NSCopying, NSMutableCopying, NSSecureCoding, NSFastEnumeration>2、自定义的对象的copy/mutablecopy
我们自定义的对象要想调用copy/mutablecopy方法,也必须手动实现NSCopying协议,实现两个方法。如果没有实现,调用copy方法会抛出异常。
定义一个person类
@interface Person : NSObject @property (strong,nonatomic) NSString* name; @property (assign,nonatomic) NSInteger age; @property (strong,nonatomic) NSArray* sons; @end //************** @implementation Person @end
使用:
1 Person* p = [[Person alloc] init]; 2 p.name = @"张三"; 3 p.age = 50; 4 p.sons = @[@"张小一",@"张小二"]; 5 Person* copy = [p copy];到5的地方抛出异常:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person copyWithZone:]: unrecognized select or sent to instance 0x8b240d0'无法识别的方法消息,这里因为person没有实现NSCopying协议和coypwithzone方法。接下来我为person类添加协议和方法。
@interface Person : NSObject<NSCopying,NSMutableCopying> ... ... ... @end //************************************** @implementation Person
-(id)copyWithZone:(NSZone *)zone
{
Person* copy = [[self class] allocWithZone:zone]; //分配新的内存空间 对于self本身来说,属于深拷贝了。
copy.name = [self.name copy]; //NSString的copy,对于name,属于浅拷贝,因为都属于不可变
copy.age = self.age;
copy.sons = [self.sons copy]; //同样浅拷贝
return self;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
Person* copy = [[self class] allocWithZone:zone];//分配新的内存空间
copy.name = [self.name mutableCopy]; //深拷贝 不可变拷贝可变
copy.age = self.age;
copy.sons = [self.sons mutableCopy]; //同样深拷贝
return copy;
}
@end
2.1使用copy:
Person* p = [[Person alloc] init]; p.name = @"张三"; p.age = 50; p.sons = @[@"张小一",@"张小二"]; Person* copy = [p copy];
打印地址:
NSLog(@"p addr = %p",p); NSLog(@"copy addr = %p",copy);
输出内容:
p addr = 0x8b30140 copy addr = 0x8d34750
我们看到,地址不同,copy的内存空间是新开辟的。这里要说一下,因为一般我们copy出来的Person对象,副本变动不希望影响到另一个。所以在copywithzone里面如上面那样操作,新开辟内存空间,逐个成员变量赋值;如果copy的对象不考虑互相影响的后果,则可以直接:
-(id)copyWithZone:(NSZone *)zone { return self; }不过这样貌似没有什么意义,不如直接用:
Person* copy = p;
Person的name和sons都属于不可变,并且调用的是copy,所以对象p和copy的这两个成员的地址应该是一样的。 我们打印出name和sons的值和地址来看一下:
NSLog(@"p.name addr = %p && copy.name addr = %p",p.name,copy.name); NSLog(@"p.sons addr = %p && copy.sons addr = %p",p.sons,copy.sons); NSLog(@"p's son addr = %p && copy's son addr = %p",[p.sons objectAtIndex:0],[copy.sons objectAtIndex:0]);输出:
p.name addr = 0x4848 && copy.name addr = 0x4848 p.sons addr = 0x8d46410 && copy.sons addr = 0x8d46410 p's son addr = 0x5858 && copy's son addr = 0x5858这个结果没有悬念,遵循不可变对象copy属于浅拷贝。
这里注意到,我专门打印出p.sons 和copy.sons的第一个元素地址来做对比,他们是一样的。NSArray是容器类型,里面的元素是在copy的时候是什么样的情况,后面详细讨论。
2.2使用mutablecopy
可以看到,copywithzone和mutablecopywithzone都创建了新的内存空间,不同之处在于对成员变量的处理上。
Person* copy = [p mutableCopy];打印结果:
p.name addr = 0x5848 && copy.name addr = 0x8a3db30 p.sons addr = 0x8a3f8f0 && copy.sons addr = 0x8a3f020 p's son addr = 0x5858 && copy's son addr = 0x5858结果是name和sons都被深拷贝,sons里面元素仍然是浅拷贝,看来NSArray的mutablecopy不会令其中的元素深拷贝。
3、容器类对象的拷贝 参考:/article/4950670.html
对于类似NSArray、NSDictionary之类的容器元素,要想实现深拷贝,可以参考一下两种方法:
1 NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],[NSStringstringWithString:@"b"],@"c",nil]; 2 NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES]; 3 NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData: 4 [NSKeyedArchiver archivedDataWithRootObject: array]];
trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。举个例子,[[array objectAtIndex:0]appendstring:@”sd”]后其他的容器内对象并不会受影响。[[array objectAtIndex:1]和[[deepCopyArray
objectAtIndex:0]尽管是指向同一块内存,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是apple的官方文档将其列为deep copy了,并添加了copy和mutablity的关系说明,故在此做一说明
相关文章推荐
- 由NSString的copy和strong/retain引出o-c的copy机制 (一)
- iOS中assign,copy,retain之间的区别以及weak和strong的区别,NSString什么时候用copy,什么时候用strong
- 关于strong retain copy 修饰nsstring
- copy,assign,strong,retain,weak,readonly,nonatomic的区别
- copy, retain, assign , readonly , readwrite,strong,weak,nonatomic整理
- Objective-C中copy 、retain以及ARC中新加入的strong、weak关键字的含义
- 关键字(nonatomic/atomic)、(copy/retain/assign/strong/weak )、(readonly / readwrite)、(@property/@
- iOS中assign,copy,retain之间的区别以及weak和strong的区别
- Objective-C学习——copy, retain,assign,readonly,readwrite,strong,weak,nonatomic,unsafe_unretained
- strong assign weak retain copy 区别
- copy,assign,strong,retain,weak,readonly,nonatomic的区别
- 为啥NSString的属性要用copy而不用retain
- NSString什么时候用copy,什么时候用strong
- COPY, RETAIN, ASSIGN , READONLY , READWRITE,STRONG,WEAK,NONATOMIC整理
- iOS - property,strong,weak,retain,assign,copy,nomatic 的区别及使用
- Nsstring定义时用copy还是用strong
- iOS开发-assign、retain、copy、strong、weak的区别
- copy, retain, assign , readonly , readwrite,strong,weak,nonatomic整理
- retain,copy,assign,strong,weak,autorelease,nonatomic,atomic等得使用及区别
- NSString属性什么时候用copy,什么时候用strong?