陰差陽(yáng)錯(cuò)载弄,前兩天和一個(gè)小伙伴在一起聊天。聊到關(guān)于copy和strong的問(wèn)題叫榕。這個(gè)在ARC[Automatic Reference Counting)]下慢慢淡化的一個(gè)東東熄赡。交流之中讓我受益匪淺,原來(lái)copy和strong還可以這么玩兼贸。
以下內(nèi)容在demo中均有體現(xiàn)
1.首先我們先看一下到底出現(xiàn)了什么問(wèn)題
使用copy修飾這個(gè)可變數(shù)組
@property (copy, nonatomic) NSMutableArray *copAry;
// 直接崩潰測(cè)試- (void)testCash {? ? NSMutableArray *arr = [NSMutableArray arrayWithObjects:@1, @2, @3, nil];self.copAry = arr;? ? [self.copAry removeObject:@1];// 直接崩潰NSLog(@"self.copyAry = %@",self.copAry);}報(bào)錯(cuò)reason:'-[__NSArrayI removeObject:]: unrecognized selector sent to instance 0x6000000487c0'
那么為什么使用strong來(lái)修飾就不會(huì)有這個(gè)問(wèn)題呢段直。
我們來(lái)看一個(gè)例子:
定義一個(gè)CJPerson類,.m 中先不實(shí)現(xiàn)setterr方法
CJPerson.h
在控制器看一下打印結(jié)果
NSMutableArray*names = [@[@"zhangsan"] mutableCopy];? ? CJPerson *person = [[CJPerson alloc] init];? ? person.sAry = names;// strongperson.cAry = names;// copy[names addObject:@"lisi"];NSLog(@"sAry = %@, cAry = %@", person.sAry, person.cAry);/* 輸出結(jié)果:
? ? sAry = (
? ? ? ? zhangsan,
? ? ? ? lisi
? ? ), cAry = (
? ? ? ? zhangsan
? ? )
? ? */
歸根結(jié)底之所以出現(xiàn)這樣問(wèn)題溶诞,那是因?yàn)锳RC情況下strong和copy對(duì)屬性setter方法重寫的區(qū)別鸯檬。
strong:setter時(shí)調(diào)用了[sAry retain]方法,實(shí)現(xiàn)了指針拷貝螺垢,也就是淺拷貝喧务。
copy:setter時(shí)調(diào)用了[cAry copy]方法,實(shí)現(xiàn)了內(nèi)容拷貝甩苛,也就是深拷貝蹂楣。
也就是在 .m 文件中系統(tǒng)默認(rèn)幫我們實(shí)現(xiàn)的是:
CJPerson.m
具體可以看內(nèi)存地址圖詳解:(簡(jiǎn)單粗糙,歡迎大神指正)
內(nèi)存地址分析.png
2.那么這個(gè)與copy修飾NSMutableArray導(dǎo)致崩潰有什么關(guān)系呢讯蒲?
原來(lái)不管是集合類對(duì)象(NSArray,NSDictionary,NSSet...)痊土,還是非集合類對(duì)象(NSString),接收到copy或者mutableCopy消息時(shí),都需遵循以下準(zhǔn)則:
copy返回的都是不可變對(duì)象墨林,所以如果對(duì)copy返回值去調(diào)用可變對(duì)象的接口就會(huì) crash
mutableCopy 返回的都是可變對(duì)象
所以在- (void)testCash方法中執(zhí)行到self.copAry = arr;ARC 環(huán)境下setter方法執(zhí)行了copy方法赁酝,導(dǎo)致原本NSMutableArray類型數(shù)組變成NSArray類型犯祠,在調(diào)用removeObject:方法時(shí),自然會(huì)出現(xiàn)這個(gè)錯(cuò)誤reason: '-[__NSArrayI removeObject:]: unrecognized selector sent to instance 0x6000000487c0'
3.那么NSArray類型為什么需要使用copy來(lái)修飾
我們繼續(xù)使用copy和strong來(lái)定義變量
@property(strong,nonatomic)NSArray*arr1;@property(copy,nonatomic)NSArray*arr2;
// 為什么 NSArray 需要使用 copy- (void)testUserCopyWithAry {NSMutableArray*arr = [NSMutableArrayarrayWithObjects:@(1), @(2), @(3),nil];self.arr1 = arr;self.arr2 = arr;? ? ? ? [arr addObject:@(4)];NSLog(@"arr1 = %@, arr2 = %@",self.arr1,self.arr2);/* 輸出結(jié)果:
? ? arr1 = (
? ? ? ? 1,
? ? ? ? 2,
? ? ? ? 3,
? ? ? ? 4
? ? ), arr2 = (
? ? ? ? 1,
? ? ? ? 2,
? ? ? ? 3
? ? )
? ? */}
簡(jiǎn)直白的說(shuō)就是:如果定義一個(gè)數(shù)組酌呆,使用strong來(lái)修飾的話衡载,如果這個(gè)數(shù)組在外界被修改的話,那么這個(gè)用strong修改的數(shù)組變量的值也會(huì)跟著變化隙袁。為什么痰娱?還是因?yàn)閟trong進(jìn)行了指針拷貝。在內(nèi)存中菩收,兩個(gè)變量指向的是同一塊內(nèi)存地址梨睁。所以為了避免值在外接發(fā)生改變而影響自身值的變化,我們通常選擇使用copy進(jìn)行修飾娜饵。
4.接著再上兩個(gè)測(cè)試?yán)悠潞兀瑢?duì)比看輸出結(jié)果
- (void)test01 {NSArray*array = @[@1,@2,@3,@4];NSArray*copyArr = [arraycopy];NSArray*mCopyArr = [array mutableCopy];NSMutableArray*mcArr = [arraycopy];NSMutableArray*mmCopyArr = [array mutableCopy];NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);/*? 輸出結(jié)果
? ? array:? ? 0x60000024ce10
? ? copyArr:? 0x60000024ce10
? ? mCopyArr:? 0x60000024cd80
? ? mcArr:? ? 0x60000024ce10
? ? mmCopyArr: 0x60000024ce70
? ? */}- (void)test02 {NSArray*tarray = @[@1,@2,@3,@4];NSMutableArray*array = [[NSMutableArrayalloc] init];? ? [array addObjectsFromArray:tarray];NSArray*copyArr = [arraycopy];NSArray*mCopyArr = [array mutableCopy];NSMutableArray*mcArr = [arraycopy];NSMutableArray*mmCopyArr = [array mutableCopy];NSLog(@"array:%p--copyArr:%p--mCopyArr:%p--mcArr:%p---mmCopyArr:%p",array,copyArr,mCopyArr,mcArr,mmCopyArr);/* 輸出結(jié)果
? ? array:? ? 0x60000024cd20
? ? copyArr:? 0x60000024cc90
? ? mCopyArr:? 0x60000024ce40
? ? mcArr:? ? 0x60000024cde0
? ? mmCopyArr: 0x60000024d020
? ? */}
小結(jié)一下:
NSArray的copy ---->指針拷貝---->淺拷貝
NSArray的mutableCopy,NSMutableArray的copy, NSMutableArray的mutableCopy 均為深拷貝箱舞,即內(nèi)容拷貝遍坟。
關(guān)于NSString(非集合類對(duì)象),NSDictionary及其對(duì)應(yīng)的可變類型都可以此類推。
5.淺拷貝晴股、深拷貝
全篇在講淺拷貝愿伴、深拷貝,追究他們追究到底是什么队魏。
段子手的理解:淺復(fù)制好比你和你的影子公般,你完蛋,你的影子也完蛋胡桨。深復(fù)制好比你和你的克隆人官帘,你完蛋,你的克隆人還活著昧谊。
淺拷貝
淺拷貝就是對(duì)內(nèi)存地址的復(fù)制刽虹,目標(biāo)對(duì)象和原對(duì)象指向同一片內(nèi)存地址。
注意
多個(gè)對(duì)象共用一塊地址時(shí)呢诬,當(dāng)內(nèi)存銷毀的時(shí)候涌哲,指向這片內(nèi)存地址的幾個(gè)指針需要重新定義才可以使用,否則出現(xiàn)野指針現(xiàn)象尚镰。
在iOS的淺拷貝中阀圾,通常會(huì)使用retain關(guān)鍵字進(jìn)行引用計(jì)數(shù)。因?yàn)樗瓤梢宰寧讉€(gè)指針共同指向同一內(nèi)存地址狗唉,也可以在release的時(shí)候 由于計(jì)數(shù)的存在初烘,不會(huì)讓內(nèi)存銷毀,從而出現(xiàn)野指針的現(xiàn)象。
深拷貝
深拷貝也就是內(nèi)容拷貝肾筐。目標(biāo)對(duì)象雖然和原對(duì)象的值一樣哆料,但是所指向的內(nèi)存地址不一樣÷痤恚可以說(shuō)深拷貝把原對(duì)象地址也拷貝了东亦,而內(nèi)存地址是自主分配的。因內(nèi)存地址不一樣唬渗,兩個(gè)對(duì)象也就互不影響典阵、互不干涉了。
在iOS的深拷貝中谣妻,通常會(huì)使用copy和mutableCopy方法
// 深拷貝- (void)testDeepCopy {NSString*str =@"abcdefg";NSString*cStr = [strcopy];NSMutableString*mStr = [str mutableCopy];? ? [mStr appendString:@"!!"];NSLog(@"\n str = %@ = %p,\n cStr = %@ = %p,\n mStr = %@ = %p", str, str, cStr, cStr, mStr, mStr);}/* 輸出結(jié)果:
str = abcdefg = 0x109dd8090,
cStr = abcdefg = 0x109dd8090,
mStr = abcdefg!! = 0x604000057e50
*/
再次驗(yàn)證了第 4 個(gè)模塊
番外篇
淺拷貝的retain和 深拷貝中提到的copy有什么區(qū)別呢
可以觀看這篇文章:copy 和 retain 到底有啥區(qū)別
6.拷貝構(gòu)造
當(dāng)然在iOS中并不是所有的對(duì)象都支持copy萄喳,mutableCopy卒稳,遵守NSCopying協(xié)議的類可以發(fā)送copy消息蹋半,遵守NSMutableCopying協(xié)議的類才可以發(fā)送mutableCopy消息。
假如發(fā)送了一個(gè)沒有遵守上訴兩協(xié)議而發(fā)送copy或者mutableCopy, 那么就會(huì)發(fā)生異常充坑。但是默認(rèn)的iOS類并沒有遵守這兩個(gè)協(xié)議减江。如果想自定義一下copy那么就必須遵守NSCopying, 并且實(shí)現(xiàn)copyWithZone:方法,如果想自定義一下mutableCopy那么就必須遵守NSMutableCopying, 并且實(shí)現(xiàn)mutableCopyWithZone:方法捻爷。
自定義 model辈灼,遵守NSCopying和NSMutableCopying協(xié)議
@interfaceCJObj:NSObject@property(copy,nonatomic)NSString*name;@property(copy,nonatomic)NSString*icon;@end
實(shí)現(xiàn):
CJObj.m
在ViewController中打印結(jié)果
// 拷貝構(gòu)造- (void)copyConstruct {? ? CJObj *obj = [[CJObj alloc] init];? ? obj.name =@"zhangsan";? ? obj.icon =@"icon";NSMutableArray*arr = [NSMutableArrayarray];NSMutableArray*copyAry = [NSMutableArrayarray];? ? [arr addObject:obj];? ? ? ? [copyAry addObject:[arr[0]copy]];//[copyAry addObject:[arr[0] mutableCopy]];CJObj *obj2 = arr[0];? ? obj2.name =@"lisi";? ? obj2.icon =@"obj2_icon";NSLog(@"arr.name = %@, arr.icon = %@", ((CJObj *)arr[0]).name, ((CJObj *)arr[0]).icon);NSLog(@"copyArr.name = %@, copyArr.icon = %@", ((CJObj *)[copyAry objectAtIndex:0]).name, ((CJObj *)[copyAry objectAtIndex:0]).icon);/* 輸出結(jié)果:
? ? ? ? arr.name = lisi, arr.icon = obj2_icon
? ? ? ? copyArr.name = zhangsan, copyArr.icon = icon
? ? */}
內(nèi)存地址圖詳解:(簡(jiǎn)單粗糙,歡迎大神指正)
拷貝構(gòu)造
以上內(nèi)容在demo中均有體現(xiàn)
感謝:
本文第3也榄、4模塊出自這里
相關(guān)閱讀:
作者:笨笨編程
鏈接:http://www.reibang.com/p/2008e585c1a0
來(lái)源:簡(jiǎn)書
簡(jiǎn)書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處甜紫。