这应该是个经典的面试题,网上也有不少相关的文章。但我表示有很多文章我都不能太认同,所以还是打算自己写下。
1.Copy#####
先看一下苹果关于copy的文档:
“The returned object is implicitly retained by the sender, who is responsible for releasing it.The copy returned is immutable”
谁copy谁负责release,返回的对象是不可变的。
下面还是直接看代码。
NSArray *arr1 = @[@1,@2];
NSArray *arr2 = [arr1 copy];
NSLog(@"\n arr1 %p \n arr2 %p",arr1,arr2);
//打印出来的内存地址
//arr1 0x100100660
//arr2 0x100100660
NSMutableArray *marr1 = @[@"one",@"two",@"three",@"four"].mutableCopy;
NSArray *arr = [marr1 copy];
NSLog(@"\n marr1 %p \n arr %p ",marr1,arr);
//打印出来的内存地址
//marr1 0x1001017c0
//arr 0x1001018d0
可以看到,不可变数组copy之后内存地址是一毛一样的,并没有在内存上重新分配空间创建副本,效果跟
NSArray *arr1 = @[@1,@2];
NSArray *arr2 = arr1;
是一样的,就是引用持有了而已。而可变数组copy之后在内存创建了新的副本。此刻需要来一张图。。
copy 引用我的理解是:反正原来的数组是不可变的,copy之后也是不可变的,就没有必要再浪费内存创建新的空间了。而原来的可变数组可能会随时改变,copy之后自然不能引用原数组。
2.MutableCopy#####
照例先看文档:
"The returned object is implicitly retained by the sender, which is responsible for releasing it. The copy returned is mutable whether the original is mutable or not."
大意是:谁污染谁治理。不管原来是不是可变,返回的是可变对象。
NSMutableArray *marr1 = @[@"one",@"two",@"three",@"four"].mutableCopy;
NSMutableArray *marr2 = [marr1 mutableCopy];
NSLog(@"\n marr1 %p \n marr2 %p ",marr1,marr2);
//result
//marr1 0x100504020
//marr2 0x100504250
NSArray *arr = @[@1,@2];
NSMutableArray *marr = [arr mutableCopy];
NSLog(@"\n arr %p \n marr %p",arr,marr);
//result
//arr 0x100102ab0
//marr 0x100102df0
再来一张图:
mutableCopy.png这就很好理解了,复制出来的对象是可变的,因此肯定要另外分配内存空间,不然只要有一个变大家都跟着变,那就没法玩了。
浅复制与深复制#####
下面我们再来看另外一段代码:
NSMutableArray *marr1 = @[@"one".mutableCopy,@"two".mutableCopy,@"three".mutableCopy,@"four".mutableCopy].mutableCopy;
NSMutableArray *marr2 = [marr1 mutableCopy];
NSMutableString *mStr = marr1[0];
[mStr appendString:@"QWE"];
NSLog(@"\n marr1 = %@ \n marr2 = %@",marr1,marr2);
//result
/*marr1 = (
oneQWE,
two,
three,
four )
marr2 = (
oneQWE,
two,
three,
four) */
可以看到,marr1[0]的值变了。因为我们从数组中获取元素时,得到的是这个元素的一个新引用,但并不是新的副本。mStr和marr1[0]指向内存中的同一个对象。修改mStr实际上就是这个对象被修改了,因此输出的marr1[0] 为"oneQWE"。
但是为什么marr2[0]的值也被改变了呢?这与默认的浅复制有关,它意味着使用mutableCopy复制时,在内存中为新的数组分配了空间,并且将单个元素复制到新数组中。然而将原始数组中的没个元素复制到新位置意味着:仅将引用从一个数组元素复制到了另一个数组元素。这样做的结果是这两个数组中的元素都指向内存中的同一个字符串。这跟一个对象赋值给另一个对象没什么两样。
从这个意义上来看,copy与mutableCopy都是浅复制。那么,怎么实现真正意义上的深复制,将marr1[0]指向的对象也进行拷贝,创建一个新副本呢?答案就是:归档。
NSMutableArray *marr1 = @[@"one".mutableCopy,@"two".mutableCopy,@"three".mutableCopy,@"four".mutableCopy].mutableCopy;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:marr1];
NSMutableArray *marr2 = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSMutableString *mStr = marr1[0];
[mStr appendString:@"QWE"];
NSLog(@"\n marr1 = %@ \n marr2 = %@",marr1,marr2);
//result
marr1 = (
oneQWE,
two,
three,
four
)
marr2 = (
one,
two,
three,
four
)
YYKit里面也有对NSObject的拓展- deepCopy
- (id)deepCopy {
id obj = nil;
@try {
obj = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self]];
}
@catch (NSException *exception) {
NSLog(@"%@", exception);
}
return obj;
}
这个这个直接拿来用。。
以上,如有错误,请大神指点。
参考书籍《Objective-C 2.0编程》
参考代码:YYKit
网友评论