或許你在iOS開發(fā)的過程中很少用到copy or mutablecopy,但是有的時候閱讀一些源碼你會遇到,然后你草率的覺得自己懂了--在心里說so esay,但是當?shù)诙斓聂[鐘叫醒你去擠地鐵時,你卻什么也想不起來。廢話說多了,這里并不是什么高超的技術(shù)難題需要我們攻克,只是理解記住。Just do it.
copy就是我們理解的復制,拷貝一個對象去創(chuàng)建一個新的對象擁有原來對象的相同的類型相同的屬性。什么時候我們會需要去copy一個對象呢?
NSMutableString *mutableTest1 = [[NSMutableString alloc]initWithString:@"我是可變字符串"];
NSString *test2 = mutableTest1;
這個時候如果我們用test2去做一些事情那么它就是不安全的,因為隨時mutableTest1有可能修改他的值,但我們僅僅想用他的值做我們的事情,這個時候就需要我們的copy。
了解淺拷貝(Shallow-copy)和深拷貝(Deep-copy)
拷貝一個對象可以淺拷貝也可以深拷貝,他們的相同點是都是直接復制他們的屬性,不同點是怎么處理指針的引用。深拷貝直接復制所引用的對象而淺拷貝則復制對象的引用。
因此,如果對象A被淺拷貝到對象B,則對象B引用對象A所指向的相同的實例變量(或?qū)傩裕?深復制對象優(yōu)先于淺復制,特別是對于值對象。深拷貝則新建一個對象,他們是不同的對象??截愅瑯訒挂糜嫈?shù)+1,所以當你拷貝一個對象的時候有責任去釋放他。
非集合對象的拷貝
顧名思義不是指集合類(數(shù)組,字典等)。首先一個對象能被拷貝需要遵循NSCopying協(xié)議并且實現(xiàn)了他的方法copyWithZone:,否則會在運行時的報錯。如果是可變對象 遵循NSMutableCopying 協(xié)議并且實現(xiàn) mutableCopyWithZone:來保證他的可變性。這時你可以利用copy和mutablecopy方法來進行復制操作,這兩個方法會直接返回上面兩個協(xié)議方法的返回值。
NSString *test1 = @"woshibukebian";
NSString *copytest1 = [test1 copy];
NSMutableString *copytest2 = [test1 copy];
NSMutableString *mutableCopytest1 = [test1 mutableCopy];
NSLog(@"%p,%@",test1,test1);
NSLog(@"%p,%@",copytest1,copytest1);
NSLog(@"%p,%@",copytest2,copytest2);
NSLog(@"%p,%@",mutableCopytest1,mutableCopytest1);
// [copytest2 appendString:@"hahhahahh"]; //報錯
[mutableCopytest1 appendString:@"hahhahahh"];
NSLog(@"%p,%@",mutableCopytest1,mutableCopytest1);
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] test1,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] copytest1,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] copytest2,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] mutableCopytest1,0x608000072700,woshibukebian
2017-01-21 13:56:03.981 CopyDemo[25182:1130625] mutableCopytest1,0x608000072700,woshibukebianhahhahahh
對于不可變的對象copy方法只是復制了對象的引用,copytest1復制了test1對象的引用,即copytest1和test1指向相同的對象保存了相同的指針,屬于淺拷貝。不可變的對象mutableCopy方法,則復制了引用的對象,重新創(chuàng)建了內(nèi)存來保存,并且對象擁有可變性。
不可變對象copy方法屬于淺拷貝僅僅是指針復制,mutableCopy方法是深拷貝會重新創(chuàng)建一個內(nèi)存復制引用的對象。
NSMutableString *test1 = [[NSMutableString alloc]initWithString:@"我是可變字符串"];
NSMutableString *copytest1 = [test1 copy];
NSMutableString *mutableCopytest1 = [test1 mutableCopy];
NSLog(@"test1,%p,%@",test1,test1);
NSLog(@"copytest1,%p,%@",copytest1,copytest1);
NSLog(@"copytest2,%p,%@",mutableCopytest1,mutableCopytest1);
[copytest1 appendString:@"11111"]; //報錯
[mutableCopytest1 appendString:@"hahhahahh"];
NSLog(@"mutableCopytest1,%p,%@",mutableCopytest1,mutableCopytest1);
2017-01-21 14:16:01.663 CopyDemo[25602:1139306] test1,0x60800007b440,我是可變字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] copytest1,0x608000050770,我是可變字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] mutableCopytest1,0x60800007b780,我是可變字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] mutableCopytest1,0x60800007b780,我是可變字符串hahhahahh
對于可變對象不論是copy還是mutableCopy方法都是深拷貝,都是重新創(chuàng)建了對象,復制的是引用的對象。copy的對象不可變,mutableCopy保持可變性。
總結(jié):不可變對象因為本身是不可變的所以僅僅復制對象的引用即指向?qū)ο蟮闹羔樉涂梢?。copy復制了對象的指針,mutableCopy拷貝了引用的對象創(chuàng)建了可變對象保存原來引用的對象。
集合對象的拷貝
最常見的拷貝是淺拷貝,創(chuàng)建一個新的集合對象和原來的對象共享集合元素。深拷貝創(chuàng)建一個新的集合并把原來的集合的元素添加到新的集合中
這有一些方法去創(chuàng)建一個淺拷貝,當你創(chuàng)建一個淺拷貝原來的對象收到retain消息同時指向?qū)ο蟮闹羔槺粡椭频叫碌募蟻肀4妗?/p>
NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];
我們可以利用copyWithZone:方法來拷貝一個集合或mutableCopyWithZone:或者initWithArray:copyItems: 方法。
這里有兩種方法來創(chuàng)建一個集合元素的深拷貝,你可以調(diào)用 initWithArray:copyItems:方法在第二個參數(shù)中傳入YES。如果你調(diào)用這個方法來創(chuàng)建一個集合的深拷貝這時會向原來集合中的所有元素發(fā)送copyWithZone:方法。如果這個元素對象沒有遵循NSCopying協(xié)議就會向我們前面描述的會在運行時報錯,然而copyWithZone:僅僅會做淺拷貝。這種拷貝僅僅創(chuàng)建了一個一層深拷貝不是完全意義上的深拷貝( This kind of copy is only capable of producing a one-level-deep copy),僅僅復制了集合中元素的指針并不是完全意義上的深拷貝,如果集合元素是一個不可變對象這可以滿足深拷貝,但如果是一個集合的集合那么集合元素同樣有可能會發(fā)生變化。
如果您需要真正意義上的深拷貝你可以利用歸檔然后解歸檔的方法來進行深拷貝。
NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:oldArray]];
但你拷貝一個集合,該集合和集合中的元素的可變性有可能會受到一些影響,不同的方法對應不同影響。
- copyWithZone: 僅僅是表面的不可變,所有深層的可變性任然保留。
- initWithArray:copyItems: with NO 賦予表面的可變性和原來創(chuàng)建的一樣,但是深層的可變性任然保持。
- initWithArray:copyItems: with YES 賦予表面的可變性和原來創(chuàng)建的一樣,但是下一層的為不可變,所有的更深的層次保持原來的可變性。
- 歸檔和解歸檔會保留原來的所有的可變性,因為創(chuàng)建了全新的對象保留了原來的數(shù)據(jù)與他的可變性。
希望到現(xiàn)在能對copy有了更深入的了解,當然我的文字功底不好或解釋與理解有偏差,希望大家指出。祝大家技術(shù)節(jié)節(jié)高!
努力的日子就是最美好的日子。——喬治亞·歐姬芙