iOS Copy的理解

iOS Copy 测试:

测试代码:

// 测试Copy

NSMutableDictionary *dic1 = [NSMutableDictionary new];
NSMutableDictionary *dic2 = [dic1 copy];
NSLog(@"dic1:%p ---- dic2:%p:", dic1, dic2);
[dic2 setObject:@"test"forKey:@"test"];//会报错No Selector,因为该dic2指向的是NSDictionary对象,没有setObject这个方法

NSDictionary *dic3 = [NSDictionary dictionaryWithObjectsAndKeys:@"dfd",@"dsfds",nil];
NSDictionary *dic4 = [dic3 copy];
NSMutableDictionary *dic5 = [dic3 mutableCopy];
NSLog(@"dic3:%p ---- dic4:%p: ---- dic5:%p", dic3, dic4, dic5);

输出结果:

dic1:0x618000244260 ---- dic2:0x60800000dbf0:

dic3:0x60000002ff60 ---- dic4:0x60000002ff60: ---- dic5:0x60800005d490


Copy结论:

(1)Copy 拷贝的是不可编辑的对象,比如NSMutableDictionary 对象 的Copy会返回一个NSDictionary对象。

(2)Mutable对象 Copy到其他对象的话,是深拷贝,会新建一块内存,并把这个内存起始地址赋值给新对象;如果是不可变对象的Copy,那就是浅拷贝。因为Copy本身是拷贝一块不可变的对象,那么如果从不可变的对象(NSDictionary)拷贝,那么系统会默认给其他对象只是显示内存数值操作,所以只需要浅拷贝一个内存地址就可以实现,减少开辟内存花销(不论是不是同一个NSZone);而如果是从mutable可变对象拷贝copy,因为存在数据同步的问题(比如mutable修改了数据),那么就需要新建一块内存复制内容。

(3)mutableCopy一定是深拷贝。


另外copy 和metableCopy  对容器(Array或者Dictionary)不论是深拷贝或者浅拷贝,对于内部元素是指针拷贝(浅拷贝,retainCount+=1)。Apple文档将这个称为集合的单层深拷贝

浅复制(shallow copy):在浅复制操作时,对于被复制对象的每一层都是指针复制。
深复制(one-level-deep copy):在深复制操作时,对于被复制对象,至少有一层是深复制。(比如Array只深复制Array的内存地址,里面数组元素浅拷贝)
完全复制(real-deep copy):在完全复制操作时,对于被复制对象的每一层都是对象复制。

集合的完全复制有两种方法:

(1)调用对象本身写好的API(如initWithArray: copyItems:   和  initWithDictionary: copyItems:,最后的参数copyItems设置为YES)

如果用这种方法深复制,集合里的每个对象都会收到 copyWithZone: 消息。如果集合里的对象遵循 NSCopying 协议,那么对象就会被深复制到新的集合(遵循上面的copy,不是mutableCopy,所以copy出来的是immutable,因此对于集合里的mutable对象,还是需要浅拷贝或者特殊处理mutableCopy)。如果对象没有遵循 NSCopying 协议,而尝试用这种方法进行深复制,会在运行时出错。

(2) 将集合进行归档(archive),然后解档(unarchive)

NSArray *trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

可以实现完全深复制,mutable对象会进行mutableCopy,而immutable对象会新建一块内存复制内容


// ---------------------------------------------------------------------------------------------------------------------------------------

// 测试新建内存

NSDictionary *dic6 = [NSDictionary new];
NSDictionary *dic7 = [NSDictionary new];
NSDictionary *dic8 = [NSDictionary dictionaryWithObject:@"test"forKey:@"test"];
NSLog(@"dic6:%p ---- dic7:%p: ---- dic8:%p", dic6, dic7, dic8);

NSMutableDictionary *dic9 = [NSMutableDictionary new];
NSMutableDictionary *dic10 = [NSMutableDictionary new];
NSMutableDictionary *dic11 = [NSMutableDictionary dictionaryWithObject:@"test"forKey:@"test"];
NSLog(@"dic9:%p ---- dic10:%p: ---- dic11:%p", dic9, dic10, dic11);
NSString *str5 = @"ddd";
NSString *str6 = @"ddd";
NSLog(@"str5:%p ---- str6:%p", str5, str6);

输出结果

dic6:0x61000000a030 ---- dic7:0x61000000a030: ---- dic8:0x608000035200

dic9:0x60000004a410 ---- dic10:0x610000049840: ---- dic11:0x6100000497b0

str5:0x105c8aa10 ---- str6:0x105c8aa10


新建内存结论:

(1)当编译器编译时发现新建OC对象指向的将是同一块内容的时候,那么系统将只开辟一块内存,然后把这个内存起始地址赋值给两个对象,所以dic9和dic10指向的地址是一样的。(str5和str6)


延伸:

实现深拷贝需要实现NSCopying、NSMutableCopying协议,当实现copy的时候,就是调用- (id)copyWithZone:(NSZone *)zone 方法。

copyWithZone返回不可变对象,而mutableCopyWithZone返回可变对象。

NSCopying和NSMutableCopying协议

@protocol NSCopying
- (id)copyWithZone:(nullableNSZone *)zone;
@end

@protocol NSMutableCopying
- (id)mutableCopyWithZone:(nullableNSZone *)zone;
@end


如果是自定义的继承类(如继承NSObject)MyObject,那么子类MyObject对象使用Copy时会报错(No Selector),这是因为子类里面没实现copyWithZone这个方法;子类里面重写copyWithZone:

类似 @interface NSString :NSObject <NSCopying,NSMutableCopying,NSSecureCoding>

@interface MyObject : NSObject<NSCopying, NSMutableCopying>
- (id)copyWithZone:(NSZone*)zone
{
    NSLog(@"zone:%p ------- defaultZone:%p", zone,NSDefaultMallocZone());
    /** 如果MyObject的copyWithZone不需要被子类调用,可以直接用MyObject */
    MyObject obj =[[MyObject allocWithZone:zone] init];
    /** 如果MyObject的copyWithZone会在子类中被调用,那么需要用[self class],这样的话,在编译时[self class]会自动编译成当前调用的类名 */
    MyObject obj = [[[self class] allocWithZone:zone] init];
 
    obj.str = [self.str copy];
    return obj;
}
 

输出结果:

zone:0x0 ------- defaultZone:0x10e569000


为什么copyWithZone的参数zone输出地址是为空,下一篇Zone重点说明吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值