许久没写博客,期间一边学习一边工作,非常忙碌,特别是iOS的学习要花很多时间,每天都要码代码,熟悉Xcode的使用,偶尔遇到一些比较大的难题,才会停下来想想怎么解决,最近在看别人源码的时候一直被copy和木tablecopy困扰,网上找的文章写的都挺好,但跟我自己做的实验不大一样,所以记录一下我做的实验结果。
一、问题
我是看着copy和mutableCopy然后糊涂了,不知道怎么用才搜的答案,而实际使用中为什么要用copy呢?以下是原因:
比如我们想拿到数组array中的下标为0的对象出来使用,那么就有以下语句:
id temp = [array objectAtIndex:0];
而如果我后面不想用这个数组或者不小心删了数组中下标为0的对象后,
[array removeObjectAtIndex:0];
上面的temp就没法使用,成了野指针,这时候再用这个temp程序就会崩。
任何技术的出现都是有理由的,copy和mutableCopy也一样,就是为了解决这里的问题,具体怎么解决继续看后面的介绍。
二、简介
先交代下copy和mutablecopy的知识,在iOS中遵守了NSCopying协议的类才能使用copy,遵守了NSMutableCopy协议的类才能使用mutableCopy,这两个协议还算比较常见,NSString、NSNumber等基础框架类一般都会有(要是实际使用中不知道可以按住command然后点击相应的类名去看看)。
如果不遵守这两个协议则不能发送copy和mutableCopy,所以想在自定义的类中实现的话要遵守这个协议,并且实现对应的方法:
- copy——copyWithZone
- mutableCopy ——mutableCopyWithZone
三、分类说明
3.1 非容器类的对象
非容器类对象主要是指NSString、NSNumber、NSData之类,这类对象也有可变和不可变之分。在拷贝之前,需要明白,拷贝的对象是什么,拷贝后要赋值的对象又是什么。我们根据拷贝的对象是什么,可以分为两类,即拷贝的对象是可变(mutable)的还是不可变(imutable)的。
3.1.1 先看看拷贝的对象是不可变对象(imutable)的情况:
NSString *string = @"Origin";
NSString *stringCopy = [string copy];
NSString *stringMCopy = [string mutableCopy];
NSMutableString *mStringCopy = [string copy];
NSMutableString *mStringMCopy = [string mutableCopy];
//[mStringCopy appendString:@"!!"];
[mStringMCopy appendString:@"!!"];
NSLog(@"string--------%p", string);
NSLog(@"stringCopy----%p", stringCopy);
NSLog(@"stringMCopy---%p", stringMCopy);
NSLog(@"mStringCopy---%p", mStringCopy);
NSLog(@"mStringMCopy--%p", mStringMCopy);
2015-09-18 11:02:15.098 CopyAndMutableCopyTest[839:51989] string--------0x100001040
2015-09-18 11:02:15.099 CopyAndMutableCopyTest[839:51989] stringCopy----0x100001040
2015-09-18 11:02:15.099 CopyAndMutableCopyTest[839:51989] stringMCopy---0x100206b70
2015-09-18 11:02:15.099 CopyAndMutableCopyTest[839:51989] mStringCopy---0x100001040
2015-09-18 11:02:15.099 CopyAndMutableCopyTest[839:51989] mStringMCopy--0x100206c00
从打印出来的信息可以看到
string--------0x100001040
stringCopy----0x100001040
mStringCopy---0x100001040
这三个都是同个内存地址,而这三个string都是通过[string copy]来赋值的,所以可以知道对非容器类的不可变对象的copy,都是浅拷贝(指针复制)。
而
stringMCopy---0x100206b70
mStringMCopy--0x100206c00
这两个string的内存地址不一样,他们是通过 [string mutableCopy] 来赋值的,所以进行了对象复制,即stringMCopy和mStringMCopy这两个对象都是重新在堆中重新开辟内存然后赋给他们的,而不是简单的指针复制,所以我们可以得到:对非容器类不可变对象的mutableCopy,都是深拷贝(对象复制)。
3.1.2 拷贝的对象是可变(mutable)对象的情况:
NSMutableString *mstring = [NSMutableString stringWithString:@"Origin"];
NSString *stringCopy = [mstring copy];
NSString *stringMCopy = [mstring mutableCopy];
NSMutableString *mStringCopy = [mstring copy];
NSMutableString *mStringMCopy = [mstring mutableCopy];
//[mStringCopy appendString:@"!!"];
[mStringMCopy appendString:@"!!"];
NSLog(@"mstring--------%p", mstring);
NSLog(@"stringCopy----%p", stringCopy);
NSLog(@"stringMCopy---%p", stringMCopy);
NSLog(@"mStringCopy---%p", mStringCopy);
NSLog(@"mStringMCopy--%p", mStringMCopy);
2015-09-18 11:04:13.295 CopyAndMutableCopyTest[847:53197] mstring--------0x100206b30
2015-09-18 11:04:13.296 CopyAndMutableCopyTest[847:53197] stringCopy----0x6e696769724f65
2015-09-18 11:04:13.296 CopyAndMutableCopyTest[847:53197] stringMCopy---0x100206c80
2015-09-18 11:04:13.296 CopyAndMutableCopyTest[847:53197] mStringCopy---0x6e696769724f65
2015-09-18 11:04:13.297 CopyAndMutableCopyTest[847:53197] mStringMCopy--0x100206cc0
从打印出来的信息可以看到
stringCopy----0x6e696769724f65
mStringCopy---0x6e696769724f65
这两个string是通过[string copy]赋值的,但是!!这个地址已经和拷贝的主体mstring的地址不一样了,mstring--------0x100206b30,所以对于非容器类的可变对象的copy,都是深拷贝(对象复制)。
而
stringMCopy---0x100206c80
mStringMCopy--0x100206cc0
这两个string是通过[string mutableCopy]赋值的,但是这两个地址不一样,可以看到是递增的,从c80增加到cc0,那么我们可以得出结论,对于非容器类的可变对象的mutableCopy,都是深拷贝(对象复制)。
这时候的[string copy] 和 [string mutableCopy]的区别也显而易见,
通过[string copy]拷贝出来的地址都是同一个0x6e696769724f65
而通过[string mutableCopy]拷贝出来的地址则会变化。
这就是在非容器类的可变对象的进行copy和mutableCopy后的区别。实际使用中还是当然还是建议大家用mutableCopy了。
注:不管是可变还是不可变对象的copy,当其赋值给mutable对象时,这个对象指向的内容是不可变的。举例子:上面的
//[mStringCopy appendString:@"!!"];
这一句编译能通过,但是运行时就会崩。所以请留意copy后的对象是不可变的,不要赋值给可变的对象。