iOS中的深拷贝和浅拷贝的学习记录

12 篇文章 0 订阅

最近想了解一下这方面的知识,所以特别记录一下


copy与retain的区别:

copy是创建一个新对象,retain是创建一个指针,引用对象计数加1。Copy属性表示两个对象内容相同,新的对象retain为1 ,与旧有对象的引用计数无关,旧有对象没有变化。copy减少对象对上下文的依赖。retain属性表示两个对象地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1也就是说,retain 是指针拷贝,copy 是内容拷贝。


深拷贝的优势就是可以完全和之前的对象分离开来.


在ios中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying 协议的类可以发送copy消息,遵守NSMutableCopying 协议的类才可以发送mutableCopy消息。假如发送了一个没有遵守上诉两协议而发送 copy或者 mutableCopy,那么就会发生异常。但是默认的ios类并没有遵守这两个协议。如果想自定义一下copy 那么就必须遵守NSCopying,并且实现 copyWithZone: 方法,如果想自定义一下mutableCopy 那么就必须遵守NSMutableCopying,并且实现 mutableCopyWithZone: 方法。


1.     系统的非容器类对象(NSString,NSNumber等等)


NSString *string = @"深拷贝与浅拷贝";
NSString *stringCopy = [string copy];
NSMutableString *stringMCopy = [string mutableCopy];

NSLog(@"%p",string);
NSLog(@"%p",stringCopy);
NSLog(@"%p",stringMCopy);

可以看到结果如下



另外唠叨一句,如果此时打印retainCount你会发现计数是



这是为什么呢,因为实际上NSString 已经autorelease了,就是说retainCount 对于autorelease消息产生的的对象,并不可靠。所以计数是没有意义的.


NSMutableString *string = [NSMutableString stringWithString: @"origion"];
    NSString *stringCopy = [string copy];
    NSMutableString *mStringCopy = [string copy];
    NSMutableString *stringMCopy = [string mutableCopy];
    [mStringCopy appendString:@"mm"];//error
    [string appendString:@" origion!"];
    [stringMCopy appendString:@"!!"];
    
    NSLog(@"%p",string);
    NSLog(@"%p",mStringCopy);
    NSLog(@"%p",stringCopy);
    NSLog(@"%p",stringMCopy);



结论:以上四个NSString对象所分配的内存都是不一样的。但是对于mStringCopy其实是个不可变对象,所以上述会报错。对于系统的非容器类对象,我们可以认为,如果对一不可变对象复制,copy是指针复制(浅拷贝)和mutableCopy就是对象复制(深拷贝)。如果是对可变对象复制,都是深拷贝,但是copy返回的对象是不可变的。


2.     系统的容器类对象 

//copy返回不可变对象,mutablecopy返回可变对象
    NSArray *array1 = [NSArray arrayWithObjects:@"a",@"b",@"c",nil];
    NSArray *arrayCopy1 = [array1 copy];
    //arrayCopy1是和array同一个NSArray对象(指向相同的对象),包括array里面的元素也是指向相同的指针
    NSLog(@"array1 retain count: %d",[array1 retainCount]);
    NSLog(@"array1 retain count: %d",[arrayCopy1 retainCount]);
    
    NSLog(@"array1地址 : %p",array1);
    NSLog(@"arrayCopy1地址 : %p",arrayCopy1);
    
    NSMutableArray *mArrayCopy1 = [array1 mutableCopy];
    //mArrayCopy1是array1的可变副本,指向的对象和array1不同,但是其中的元素和array1中的元素指向的是同一个对象。mArrayCopy1还可以修改自己的对象
    [mArrayCopy1 addObject:@"de"];
    [mArrayCopy1 removeObjectAtIndex:0];

    NSLog(@"mArrayCopy1地址 : %p",mArrayCopy1);
    
    NSLog(@"array1=%@",array1);
    NSLog(@"arrayCopy1=%@",arrayCopy1);
    NSLog(@"mArrayCopy1=%@",mArrayCopy1);



array1和arrayCopy1是指针复制,而mArrayCopy1是对象复制,mArrayCopy1还可以改变期内的元素:删除或添加。但是注意的是,容器内的元素内容都是指针复制。


对于容器而言,其元素对象始终是指针复制。如果需要元素对象也是对象复制,就需要实现深拷贝


NSArray *array = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"first"],@"b",@"c",nil];
    
    NSArray *deepCopyArray=[[NSArray alloc] initWithArray: array copyItems: YES];
    
    NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
                                  [NSKeyedArchiver archivedDataWithRootObject: array]];

trueDeepCopyArray是完全意义上的深拷贝,而deepCopyArray则不是,对于deepCopyArray内的不可变元素其还是指针复制。或者我们自己实现深拷贝的方法。因为如果容器的某一元素是不可变的,那你复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。


3.     自定义对象

如果是我们定义的对象,那么我们自己要实现NSCopying,NSMutableCopying这样就能调用copy和mutablecopy了。

@interface MyObj : NSObject<NSCopying,NSMutableCopying>
{
         NSMutableString *name;
         NSString *imutableStr;
         int age;
}
@property (nonatomic, retain) NSMutableString *name;
@property (nonatomic, retain) NSString *imutableStr;
@property (nonatomic) int age;
@end
@implementation MyObj
@synthesize name;
@synthesize age;
@synthesize imutableStr;
- (id)init
{
         if (self = [super init])
         {
                   self.name = [[NSMutableString alloc]init];
                   self.imutableStr = [[NSString alloc]init];
                   age = -1;
         }
         return self;
}
- (void)dealloc
{
         [name release];
         [imutableStr release];
         [super dealloc];
}
- (id)copyWithZone:(NSZone *)zone
{
         MyObj *copy = [[[self class] allocWithZone:zone] init];
         copy->name = [name copy];
         copy->imutableStr = [imutableStr copy];
//       copy->name = [name copyWithZone:zone];;
//       copy->imutableStr = [name copyWithZone:zone];//
         copy->age = age;
         return copy;
}
- (id)mutableCopyWithZone:(NSZone *)zone
{
         MyObj *copy = NSCopyObject(self, 0, zone);
         copy->name = [self.name mutableCopy];
         copy->age = age;
         return copy;
}

非指针型实例变量没有浅复制与深复制之分,像布尔型、整型、浮点型。浅复制与深复制是针对指针型实例变量说的,浅复制就只是复制了指针到副本中,原始对象与副本共享内存数据;深复制就是把内存的资源也复制了,原始对象和副本分别对应自己的内存数据。


最后再写一个例子

NSMutableDictionary *srcDic = [NSMutableDictionary dictionaryWithObjectsAndKeys:@"value", @"key", nil];  
NSMutableDictionary *dicCopy = [srcDic copy];//注意这里对NSMutableDictionary进行copy操作  
NSLog(@"srcDic:%p, %@", srcDic, srcDic);  
NSLog(@"dicCopy:%p %@", dicCopy, dicCopy);  
[dicCopy setObject:@"newValue" forKey:@"key"];//尝试修改dicCopy里面的值  

结果:
srcDic:0x7144ea0, {  
    key = value;  
}  
dicCopy:0x71445c0 {  
    key = value;  
}  
[__NSDictionaryI setObject:forKey:]: unrecognized selector sent to instance 0x71445c0  


//浅拷贝
    NSArray *copyArr = [[NSArray alloc]initWithArray:@[@(1)]];
    NSDictionary *copyDict = [[NSDictionary alloc]initWithDictionary:@{@"1":@(1),@"3":@(2)}];
    
    //深拷贝
    NSArray *copyArrDeep = [[NSArray alloc]initWithArray:@[@(1)] copyItems:YES];
    NSDictionary *copyDictDeep =[[NSDictionary alloc]initWithDictionary:@{@"1":@(1),@"3":@(2)} copyItems:YES];

    //归档
    //保存
    NSData *buffer = [NSKeyedArchiver archivedDataWithRootObject:@[@(1),@(2)]];
    //恢复
    NSArray *arr = [NSKeyedUnarchiver unarchiveObjectWithData:buffer];



结论:

由[srcDic mutableCopy]得到的dicCopy,两者内存地址不一致,即是copy对NSMutableDictionary类型进行了深复制,当尝试修改dicCopy里面的值时,发现报错了,修改不了,可以确定副本dicCopy是不可变副本。也就是说,copy一个Mutable类型可以深复制,但是其返回的 还是一个不可变的类型.


--------- 2月4日------------------------




//对mutable数组copy
	NSMutableArray *a = [[NSMutableArray alloc]initWithObjects:@"1", nil];
    NSLog(@"a-p %p",a);
    NSLog(@"a %d",[a retainCount]);
    
    NSArray *b = [a copy];
    NSMutableArray *c = [a mutableCopy];
    
    NSLog(@"a %d",[a retainCount]);
    NSLog(@"b %d",[b retainCount]);
    NSLog(@"c %d",[c retainCount]);
    
    NSLog(@"a-p %p",a);
    NSLog(@"b-p %p",b);
    NSLog(@"c-p %p",c);
    
    //对不可变数组进行copy
    NSArray *a1 = [[NSArray alloc]initWithObjects:@"1", nil];
    NSLog(@"a1-p %p",a1);
    NSLog(@"a1 %d",[a1 retainCount]);
    
    NSArray *b1 = [a1 copy];
    NSMutableArray *c1 = [a1 mutableCopy];
    
    NSLog(@"a1 %d",[a1 retainCount]);
    NSLog(@"b1 %d",[b1 retainCount]);
    NSLog(@"c1 %d",[c1 retainCount]);
    
    NSLog(@"a1-p %p",a1);
    NSLog(@"b1-p %p",b1);
    NSLog(@"c1-p %p",c1);



自定义对象copy或mutablecopy都会是深复制,copy是浅复制,mutableCopy是深复制,对容器而言,copy或mutableCopy一个可变的数组都是深复制,也就是说会出现新的内存地址,对不可变数组进行copy是指针复制,也就是浅复制,mutableCopy则是深复制,字符串亦是如此.但是如果copy一个mutable对象,用可变的对象去接收,例如copy一个mutable数组,用一个mutable数组接收,实际上是错误的,因为copy出来的是不可变的对象.


再加入一个深复制 浅复制的面试题

1.Difference between shallow copy and deep copy? 


浅复制和深复制的区别?


答案:浅层复制:只复制指向对象的指针,而不复制引用对象本身。


深层复制:复制引用对象本身。


意思就是说我有个A对象,复制一份后得到A_copy对象后,对于浅复制来说,A和A_copy指向的是同一个内存资源,复制的只不过是是一个指针,对象本身资源


还是只有一份,那如果我们对A_copy执行了修改操作,那么发现A引用的对象同样被修改,这其实违背了我们复制拷贝的一个思想。深复制就好理解了,内存中存在了


两份独立对象本身。


用网上一哥们通俗的话将就是:


浅复制好比你和你的影子,你完蛋,你的影子也完蛋


深复制好比你和你的克隆人,你完蛋,你的克隆人还活着。




参考文章:retainCount=1 ios复制--深复制,浅复制,自定义对象复制 IOS开发之深拷贝与浅拷贝(mutableCopy与Copy)详解

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值