最近同事問我一個問題:原數(shù)組A缕贡,進行復(fù)制得到數(shù)組B翁授,改變數(shù)組B的Person元素對象,不影響數(shù)組A的Person元素對象晾咪,如何操作收擦?
第一感覺是進行深復(fù)制,同樣數(shù)組里面的元素對象也要進行深復(fù)制谍倦,于是就找到相關(guān)的API:
- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;
然后同事跟我說還有其他方法嗎塞赂?要不分享一下iOS的復(fù)制吧?然后就有了這篇文章昼蛀。文章如有錯誤歡迎指出更正宴猾,小弟虛心受教,也怕誤人子弟叼旋。
為什么要復(fù)制仇哆?
定義:在面向?qū)ο缶幊讨校瑢ο髲?fù)制是創(chuàng)建一個現(xiàn)有對象的副本夫植,即面向?qū)ο缶幊讨械囊粋€數(shù)據(jù)單元讹剔。生成的對象稱為對象副本或者僅僅是原始對象的副本油讯。
意義:為了操作對象副本數(shù)據(jù)時不影響原對象數(shù)據(jù)
在iOS中哪些類支持復(fù)制功能?
NSString延欠、NSMutableString陌兑、NSArray、NSMutableArray由捎、NSDictionary兔综、NSMutableDictionary…
不難發(fā)現(xiàn)在API中這些類需要遵循<NSCopying, NSMutableCopying>
。至于為什么要遵循這兩個協(xié)議狞玛,協(xié)議中需要實現(xiàn)哪些方法后面涉及软驰,這里不做闡釋。但是至少可以總結(jié)出想要類支持復(fù)制功能为居,就要遵循<NSCopying, NSMutableCopying>
以及實現(xiàn)對應(yīng)方法碌宴。
淺復(fù)制 or 深復(fù)制杀狡?
復(fù)制主要分為淺復(fù)制和深復(fù)制蒙畴。
- 淺復(fù)制:拷貝指向?qū)ο蟮闹羔槪皇菍ο蟊旧怼?/li>
- 深復(fù)制:拷貝對象內(nèi)容指向另外一塊內(nèi)存呜象。
下圖是淺復(fù)制與深復(fù)制的關(guān)系(下圖來自官方文檔)
舉個例子:
NSString *immutableStr = @"不可變字符串";
NSString *immutableStrCopy = [immutableStr copy];
NSString *immutableStrMutableCopy = [immutableStr mutableCopy];
NSLog(@"%@--%p", immutableStr, immutableStr); // name--0x100001040
NSLog(@"%@--%p", immutableStrCopy, immutableStrCopy); // name--0x100001040
NSLog(@"%@--%p", immutableStrMutableCopy, immutableStrMutableCopy); // name--0x10075b320
NSLog(@"------------------------");
NSMutableString *mutableStr = [[NSMutableString alloc] initWithString:@"可變字符串"];
NSMutableString *mutableStrCopy = [mutableStr copy];
NSMutableString *mutableStrMutableCopy = [mutableStr mutableCopy];
NSLog(@"%@--%p", mutableStr, mutableStr); // string--0x604000249a50
NSLog(@"%@--%p", mutableStrCopy, mutableStrCopy); // string--0xa00676e697274736
NSLog(@"%@--%p", mutableStrMutableCopy, mutableStrMutableCopy); // string--0x604000249720
NSLog(@"------------------------");
NSArray *immutableArray = @[@"1", @"2"];
NSArray *immutableArrayCopy = [immutableArray copy];
NSArray *immutableArrayMutableCopy = [immutableArray mutableCopy];
NSLog(@"%@--%p", immutableArray, immutableArray); // 1,2--0x60000003a200
NSLog(@"%@--%p", immutableArrayCopy, immutableArrayCopy); // 1,2--0x60000003a200
NSLog(@"%@--%p", immutableArrayMutableCopy, immutableArrayMutableCopy); // 1,2--0x600000449de0
NSLog(@"------------------------");
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:@[@"1", @"2"]];
NSMutableArray *mutableArrayCopy = [mutableArray copy];
NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
NSLog(@"%@--%p", mutableArray, mutableArray); // 1,2--0x60000005c9e0
NSLog(@"%@--%p", mutableArrayCopy, mutableArrayCopy); // 1,2--0x60000003a340
NSLog(@"%@--%p", mutableArrayMutableCopy, mutableArrayMutableCopy); // 1,2--0x60000005ca40
總結(jié):
- 不可變對象:進行copy得到的是淺復(fù)制膳凝,進行mutableCopy得到的是深復(fù)制。
- 可變對象:無論進行copy還是mutableCopy都是深復(fù)制恭陡。
類型 | copy | mutableCopy |
---|---|---|
NSString | 淺復(fù)制 | 深復(fù)制 |
NSMutableString | 深復(fù)制 | 深復(fù)制 |
NSArray | 淺復(fù)制 | 深復(fù)制 |
NSMutableArray | 深復(fù)制 | 深復(fù)制 |
... | ... | ... |
聲明類型一定是進行復(fù)制后的類型嗎蹬音?
[圖片上傳失敗...(image-7fdc29-1530757237487)]
斷點調(diào)試發(fā)現(xiàn)不是,比如:immutableStrMutableCopy聲明的是不可變類型NSString休玩,NSString類型是不可以對字符串進行增刪操作的著淆,然而NSMutableString類型卻可以。因為iOS是動態(tài)語言拴疤,運行時才斷定是什么類型永部,顯然immutableStrMutableCopy實際是NSMutableString,可以對它進行追加字符串呐矾,例如:
[(NSMutableString *)immutableStrMutableCopy appendString:@"追加字符"];
NSLog(@"%@--%p", immutableStrMutableCopy, immutableStrMutableCopy); // string追加字符--0x10075b320
相反mutableStrCopy聲明的是NSMutableString類型苔埋,實際是NSString類型,如果對mutableStrCopy進行增刪操作蜒犯,必然crash组橄。
[mutableStrCopy appendString:@"will crash"];
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0xa00676e697274736'
mutableStrCopy對象沒有appendString:這個方法。
以此類推罚随,同樣對于數(shù)組來說:
[圖片上傳失敗...(image-2dc523-1530757237487)]
顯然根據(jù)斷點信息:
immutableArrayMutableCopy是NSMutableArray玉工,可以添加新對象
[(NSMutableArray *)immutableArrayMutableCopy addObject:@"3"];
NSLog(@"%@--%p", immutableArrayMutableCopy, immutableArrayMutableCopy); // 1,2,3--0x600000449de0
mutableArrayCopy是NSArray,添加新的對象會crash
// crash
[mutableArrayCopy addObject:@"3"];
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x600000430600'
總結(jié):
- 不可變對象進行mutableCopy得到的是可變對象.
- 可變對象進行copy得到的是不可變對象淘菩。
深復(fù)制真的是深復(fù)制嗎遵班?
NSArray *immutableArray = @[@"1", @"2"];
NSArray *immutableArrayCopy = [immutableArray copy];
NSArray *immutableArrayMutableCopy = [immutableArray mutableCopy];
NSLog(@"%@--%p", immutableArray, immutableArray); // 1,2--0x60000003a200
NSLog(@"%@--%p", immutableArrayCopy, immutableArrayCopy); // 1,2--0x60000003a200
NSLog(@"%@--%p", immutableArrayMutableCopy, immutableArrayMutableCopy); // 1,2--0x600000449de0
NSLog(@"------------------------");
NSLog(@"%@--%p", [immutableArray firstObject], [immutableArray firstObject]); // 1--0x10d448078
NSLog(@"%@--%p", [immutableArrayCopy firstObject], [immutableArrayCopy firstObject]); // 1--0x10d448078
NSLog(@"%@--%p", [immutableArrayMutableCopy firstObject], [immutableArrayMutableCopy firstObject]); // 1--0x10d448078
NSLog(@"------------------------");
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:@[@"1", @"2"]];
NSMutableArray *mutableArrayCopy = [mutableArray copy];
NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
NSLog(@"%@--%p", mutableArray, mutableArray); // 1,2--0x60000005c9e0
NSLog(@"%@--%p", mutableArrayCopy, mutableArrayCopy); // 1,2--0x60000003a340
NSLog(@"%@--%p", mutableArrayMutableCopy, mutableArrayMutableCopy); // 1,2--0x60000005ca40
NSLog(@"------------------------");
NSLog(@"%@--%p", [mutableArray firstObject], [mutableArray firstObject]); // 1--0x10f1f3078
NSLog(@"%@--%p", [mutableArrayCopy firstObject], [mutableArrayCopy firstObject]); // 1--0x10f1f3078
NSLog(@"%@--%p", [mutableArrayMutableCopy firstObject], [mutableArrayMutableCopy firstObject]); // 1--0x10f1f3078
從上面的代碼篩選出深復(fù)制
的例子:
NSArray *immutableArray = @[@"1", @"2"];
NSArray *immutableArrayMutableCopy = [immutableArray mutableCopy];
NSLog(@"%@--%p", immutableArray, immutableArray); // 1,2--0x60000003a200
NSLog(@"%@--%p", immutableArrayMutableCopy, immutableArrayMutableCopy); // 1,2--0x600000449de0
NSLog(@"------------------------");
NSLog(@"%@--%p", [immutableArray firstObject], [immutableArray firstObject]); // 1--0x10d448078
NSLog(@"%@--%p", [immutableArrayMutableCopy firstObject], [immutableArrayMutableCopy firstObject]); // 1--0x10d448078
NSLog(@"------------------------");
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:@[@"1", @"2"]];
NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
NSLog(@"%@--%p", mutableArray, mutableArray); // 1,2--0x60000005c9e0
NSLog(@"%@--%p", mutableArrayMutableCopy, mutableArrayMutableCopy); // 1,2--0x60000005ca40
NSLog(@"------------------------");
NSLog(@"%@--%p", [mutableArray firstObject], [mutableArray firstObject]); // 1--0x10f1f3078
NSLog(@"%@--%p", [mutableArrayMutableCopy firstObject], [mutableArrayMutableCopy firstObject]); // 1--0x10f1f3078
發(fā)現(xiàn)深復(fù)制只作用于數(shù)組對象這層,而數(shù)組對象里面存放的元素并沒有復(fù)制。引用官方文檔里面的一句話:
“This kind of copy is only capable of producing a one-level-deep copy. If you only need a one-level-deep copy...
If you need a true deep copy, such as when you have an array of arrays…”
理解為這只是單層深復(fù)制(one-level-deep copy)
那么現(xiàn)在區(qū)分一下概念:
- 淺復(fù)制(shallow copy):在淺復(fù)制操作時费奸,對于被復(fù)制對象指針復(fù)制弥激。
- 深復(fù)制(one-level-deep copy):在深復(fù)制操作時,對于被復(fù)制對象愿阐,至少有一層是深復(fù)制微服。
- 完全復(fù)制(real-deep copy):在完全復(fù)制操作時,對于被復(fù)制對象的每一層都是對象復(fù)制缨历。
根據(jù)上圖(來自官網(wǎng))舉例例子場景說明:
- 單層深復(fù)制:數(shù)組A以蕴,進行深復(fù)制得到數(shù)組B,當(dāng)修改數(shù)組B里面的對象時辛孵,數(shù)組A里面的對象也會跟著變丛肮。
- 完全復(fù)制:數(shù)組A,進行深復(fù)制得到數(shù)組B魄缚,當(dāng)修改數(shù)組B里面的對象時宝与,數(shù)組A里面的對象不會跟著變。
那么對于集合怎樣才算是完全復(fù)制呢冶匹?
歸檔方式
NSArray *immutableArray = @[@"1", @"2"];
NSArray *immutableArrayMutableCopy = [immutableArray mutableCopy];
// 歸檔深復(fù)制
NSArray *archiverDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:immutableArray]];
NSLog(@"%@--%p", immutableArray, immutableArray); // 1,2--0x604000430900
NSLog(@"%@--%p", immutableArrayMutableCopy, immutableArrayMutableCopy); // 1,2--0x604000255300
NSLog(@"%@--%p", archiverDeepCopyArray, archiverDeepCopyArray); // 1,2--0x604000430840
NSLog(@"------------------------");
NSLog(@"%@--%p", [immutableArray firstObject], [immutableArray firstObject]); // 1--0x10d448078
NSLog(@"%@--%p", [immutableArrayMutableCopy firstObject], [immutableArrayMutableCopy firstObject]); // 1--0x10d448078
NSLog(@"%@--%p", [archiverDeepCopyArray firstObject], [archiverDeepCopyArray firstObject]); // 1--0xa000000000000311
NSLog(@"------------------------");
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:@[@"1", @"2"]];
NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
// 歸檔深復(fù)制
NSArray *archiverDeepCopyArray1 = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:mutableArray]];
NSLog(@"%@--%p", mutableArray, mutableArray); // 1,2--0x60000005c9e0
NSLog(@"%@--%p", mutableArrayMutableCopy, mutableArrayMutableCopy); // 1,2--0x60000005ca40
NSLog(@"%@--%p", archiverDeepCopyArray1, archiverDeepCopyArray1); // 1,2--0x600000253a10
NSLog(@"------------------------");
NSLog(@"%@--%p", [mutableArray firstObject], [mutableArray firstObject]); // 1--0x10f1f3078
NSLog(@"%@--%p", [mutableArrayMutableCopy firstObject], [mutableArrayMutableCopy firstObject]); // 1--0x10f1f3078
NSLog(@"%@--%p", [archiverDeepCopyArray1 firstObject], [archiverDeepCopyArray1 firstObject]); // 1--0xa000000000000311
通過上述例子使用歸檔方式可以達到完全復(fù)制习劫。
自帶API初始化方式
NSArray *immutableArray = @[@"1", @"2"];
NSArray *immutableArrayMutableCopy = [immutableArray mutableCopy];
// 深復(fù)制
// copyItems參數(shù)表示:是否里面的元素也進行復(fù)制, NO表示淺復(fù)制嚼隘, YES表示深復(fù)制
NSArray *copyItemsDeepCopyArray = [[NSArray alloc] initWithArray:immutableArray copyItems:YES];
NSArray *copyItemsDeepCopyArray2 = [[NSMutableArray alloc] initWithArray:immutableArray copyItems:YES];
NSLog(@"%@--%p", immutableArray, immutableArray); // 1,2--0x604000430900
NSLog(@"%@--%p", immutableArrayMutableCopy, immutableArrayMutableCopy); // 1,2--0x604000255300
NSLog(@"%@--%p", copyItemsDeepCopyArray, copyItemsDeepCopyArray); // 1,2--0x604000430840
NSLog(@"------------------------");
NSLog(@"%@--%p", [immutableArray firstObject], [immutableArray firstObject]); // 1--0x10d448078
NSLog(@"%@--%p", [immutableArrayMutableCopy firstObject], [immutableArrayMutableCopy firstObject]); // 1--0x10d448078
NSLog(@"%@--%p", [copyItemsDeepCopyArray firstObject], [copyItemsDeepCopyArray firstObject]); // 1--0xa000000000000311
NSLog(@"------------------------");
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:@[@"1", @"2"]];
NSMutableArray *mutableArrayMutableCopy = [mutableArray mutableCopy];
// 深復(fù)制
NSArray *copyItemsDeepCopyArray1 = [[NSMutableArray alloc] initWithArray:mutableArray copyItems:YES];
NSLog(@"%@--%p", mutableArray, mutableArray); // 1,2--0x60000005c9e0
NSLog(@"%@--%p", mutableArrayMutableCopy, mutableArrayMutableCopy); // 1,2--0x60000005ca40
NSLog(@"%@--%p", copyItemsDeepCopyArray1, copyItemsDeepCopyArray1); // 1,2--0x60000025c890
NSLog(@"------------------------");
NSLog(@"%@--%p", [mutableArray firstObject], [mutableArray firstObject]); // 1--0x10f1f3078
NSLog(@"%@--%p", [mutableArrayMutableCopy firstObject], [mutableArrayMutableCopy firstObject]); // 1--0x10f1f3078
NSLog(@"%@--%p", [copyItemsDeepCopyArray1 firstObject], [copyItemsDeepCopyArray1 firstObject]); // 1--0xa000000000000311
使用上述兩種方式均可達到完全復(fù)制的效果诽里。
有四個注意點:
- 使用歸檔方式:歸檔的對象必須遵循NSCoding協(xié)議并實現(xiàn)協(xié)議方法。
- 使用歸檔方式:使用NSKeyedArchiver歸檔的對象是什么類型飞蛹,那么NSKeyedUnarchiver解檔出來的對象就是什么類型
比如歸檔的是NSArray類型谤狡,解檔得到的類型也是NSArray:
NSArray *archiverDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:immutableArray]]; - 使用實例化方法:如果copyItems為YES,那么數(shù)組的元素對象必須遵循NSCopying協(xié)議并實現(xiàn)協(xié)議方法卧檐,否則crash
- 使用實例化方法:使用什么類型進行初始化的墓懂,得到的就是什么類型的對象。
比如使用NSMutableArray進行初始化泄隔,那么copyItemsDeepCopyArray1就是NSMutableArray類型塘砸,而不是NSArray類型:
NSArray *copyItemsDeepCopyArray1 = [[NSMutableArray alloc] initWithArray:mutableArray copyItems:YES];
如何讓對象支持復(fù)制操作照捡?
上面提及到只要遵循<NSCopying, NSMutableCopying>
以及實現(xiàn)協(xié)議方法就可以實現(xiàn)復(fù)制操作,那先看看這兩個協(xié)議有什么協(xié)議方法:
@protocol NSCopying
- (id)copyWithZone:(nullable NSZone *)zone;
@end
@protocol NSMutableCopying
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
@end
自定義Person類實現(xiàn)淺復(fù)制&深復(fù)制
// Person.h
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
// Person.m
// 深復(fù)制
- (id)copyWithZone:(NSZone *)zone{
//創(chuàng)建新的對象空間
Person *p = [[self class] allocWithZone:zone];
p.name = self.name;
p.age = self.age;
return p;
}
// 淺復(fù)制(偽復(fù)制)
//- (id)copyWithZone:(NSZone *)zone{
// // 返回對象本身
// return self;
//}
// 深復(fù)制
- (id)mutableCopyWithZone:(NSZone *)zone{
//創(chuàng)建新的對象空間
Person *p = [[self class] allocWithZone:zone];
//為每個屬性創(chuàng)建新的空間,并將內(nèi)容復(fù)制
p.name = self.name;
p.age = self.age;
return p;
}
測試:
Person *person = [[Person alloc] init];
person.name = @"daisuke";
person.age = 26;
Person *personCopy = [person copy];
Person *personMutableCopy = [person mutableCopy];
NSLog(@"%@--%p", person, person.name); // <Person: 0x6040000275e0>--0x100349088
NSLog(@"%@--%p", personCopy, personCopy.name); // <Person: 0x604000222340>--0x100349088
NSLog(@"%@--%p", personMutableCopy, personMutableCopy.name); // <Person: 0x604000222b80>--0x100349088
從打印結(jié)果的出都實現(xiàn)了深復(fù)制操作阶界。但是一般來說自定義對象不需要實現(xiàn)NSMutableCopying協(xié)議侥钳,因為對象不像容器旺入,本身沒有相關(guān)存儲擴展等功能场躯。
看到這里可能細(xì)心的人發(fā)現(xiàn)都是深復(fù)制,為什么person對象里面的name屬性地址沒有深復(fù)制湾揽?
由于NSString特殊性瓤逼,系統(tǒng)會判斷字符串屬性在同一內(nèi)容前提下笼吟,使用@“”或者initWith..方法創(chuàng)建的對象作為常量,放在常量區(qū)則不會開辟新的內(nèi)存空間霸旗。
驗證:
NSString *text = @"123";
NSLog(@"%p---", text); // 0x10f36b0c8
NSString *text1 = @"123";
NSLog(@"%p---", text1); // 0x10f36b0c8
NSString *text2 = [[NSString alloc] initWithString:@"123"];
NSLog(@"%p---", text2); // 0x10f36b0c8
發(fā)現(xiàn)不同變量贷帮,因為內(nèi)容一致,指向的地址是一樣的诱告。所以除了改變值撵枢,難道就沒有開辟新內(nèi)存空間的方法了嗎?有
NSString *text3 = [NSString stringWithFormat:@"123"];
NSLog(@"%p---", text3); // 0xa000000003332313
NSString *text4 = [[NSString alloc] initWithFormat:@"123"];
NSLog(@"%p---", text4); // 0xa000000003332313
會發(fā)現(xiàn)同樣的內(nèi)容精居,但是地址是不一樣的锄禽,而且長度也不一樣。
總結(jié):
- @“”或者initWith..方法的變量存放在常量區(qū)靴姿,由系統(tǒng)管理內(nèi)存
- Format:方式創(chuàng)建的變量存放在堆區(qū)
所以想要實現(xiàn)完全的復(fù)制可以這樣做:
// Person.m
- (id)copyWithZone:(NSZone *)zone{
//創(chuàng)建新的對象空間
Person *p = [[self class] allocWithZone:zone];
p.name = [NSString stringWithFormat:@"%@", self.name];
p.age = self.age;
return p;
}
實現(xiàn)完全復(fù)制無非就是怕修改副本對象的屬性沃但,從而影響到原對象的屬性。對于字符串來說佛吓,其實你可以不需要這樣做宵晚。因為副本對象修改字符串屬性不會影響原對象的字符串屬性。
驗證:
Person *person = [[Person alloc] init];
person.name = @"daisuke";
person.age = 26;
Person *personCopy = [person copy];
Person *personMutableCopy = [person mutableCopy];
NSLog(@"%@--%p", person, person.name); // <Person: 0x6040000275e0>--0x100349088
NSLog(@"%@--%p", personCopy, personCopy.name); // <Person: 0x604000222340>--0x100349088
NSLog(@"%@--%p", personMutableCopy, personMutableCopy.name); // <Person: 0x604000222b80>--0x100349088
personCopy.name = @"修改name的值";
NSLog(@"%@-%@-%p", person, person.name, person.name); // <Person: 0x60000003f140>-daisuke-0x103f1e088
NSLog(@"%@-%@-%p", personCopy, personCopy.name, personCopy.name); // <Person: 0x6000002302a0>-修改name的值-0x103f1e0c8
上面的代碼中看到盡管兩個對象name的地址是一樣的辈毯,但是修改對象personCopy的屬性name的值坝疼,并沒有影響到對象person的name值搜贤。
對象中有對象屬性時如何支持復(fù)制操作谆沃?
新建一個Contact類作為person的一個屬性:
// Person.h
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
/// 聯(lián)系方式
@property (nonatomic, strong) Contact *contact;
@end
在copyWithZone:方法中
// Person.m
- (id)copyWithZone:(NSZone *)zone{
//創(chuàng)建新的對象空間
Person *p = [[self class] allocWithZone:zone];
p.name = self.name;
p.age = self.age;
p.contact = self.contact;
return p;
}
測試:
Person *person = [[Person alloc] init];
person.name = @"daisuke";
person.age = 26;
Contact *contact = [[Contact alloc] init];
contact.phone = @"12345678900";
contact.email = @"feng@gmail.com";
person.contact = contact;
Person *personCopy = [person copy];
NSLog(@"%@--%p--%@--%p", person, person.name, person.contact.phone, person.contact);
NSLog(@"%@--%p--%@--%p", personCopy, personCopy.name,personCopy.contact.phone, personCopy.contact);
// <Person: 0x600000024a00>--0x10
b2a8088--12345678900--0x60000022fdc0
// <Person: 0x600000230300>--0x10b2a8088--12345678900--0x60000022fdc0
personCopy.contact.phone = @"999999999";
personCopy.name = @"這是新的名稱";
NSLog(@"%@--%p--%@--%p", person, person.name, person.contact.phone, person.contact);
NSLog(@"%@--%p--%@--%p", personCopy, personCopy.name,personCopy.contact.phone, personCopy.contact);
// <Person: 0x600000024a00>--0x10b2a8088--999999999--0x60000022fdc0
// <Person: 0x600000230300>--0x10b2a8128--999999999--0x60000022fdc0
從打印信息看到person是進行了深復(fù)制,但是對象contact指向的是同一個指針仪芒,顯然并沒有進行深復(fù)制(單層深復(fù)制)唁影,也可以從copyWithZone:方法中看到p.contact = self.contact;只是簡單的指針賦值。如果改變副本personCopy的contact對象屬性掂名,原對象的contact也會跟著改變据沈,這不是我們想要的結(jié)果。那應(yīng)該怎么辦呢饺蔑?方法是讓屬性對象也實現(xiàn)深復(fù)制功能
例如Contact也實現(xiàn)深復(fù)制:
// Contact.m
-(id)copyWithZone:(NSZone *)zone{
Contact *contact = [[self class] allocWithZone:zone];
contact.phone = self.phone;
contact.email = self.email;
return contact;
}
Contact類繼承NSCopying協(xié)議實現(xiàn)了深復(fù)制功能锌介,然后在Person類的深復(fù)制方法中修改為:
// Person.m
- (id)copyWithZone:(NSZone *)zone{
//創(chuàng)建新的對象空間
Person *p = [[self class] allocWithZone:zone];
p.name = self.name;
p.age = self.age;
// p.contact = self.contact;
// 深復(fù)制
p.contact = [self.contact copy];
return p;
}
這樣就能讓對象屬性實現(xiàn)深復(fù)制效果,驗證:
Person *person = [[Person alloc] init];
person.name = @"daisuke";
person.age = 26;
Contact *contact = [[Contact alloc] init];
contact.phone = @"12345678900";
contact.email = @"feng@gmail.com";
person.contact = contact;
Person *personCopy = [person copy];
NSLog(@"%@--%p--%@--%p", person, person.name, person.contact.phone, person.contact);
NSLog(@"%@--%p--%@--%p", personCopy, personCopy.name,personCopy.contact.phone, personCopy.contact);
// <Person: 0x60400042f820>--0x102d04088--12345678900--0x60400042f7c0
// <Person: 0x60400042f860>--0x102d04088--12345678900--0x60400042f8a0
personCopy.contact.phone = @"999999999";
personCopy.name = @"這是新的名稱";
NSLog(@"%@--%p--%@--%p", person, person.name, person.contact.phone, person.contact);
NSLog(@"%@--%p--%@--%p", personCopy, personCopy.name,personCopy.contact.phone, personCopy.contact);
// <Person: 0x60400042f820>--0x102d04088--12345678900--0x60400042f7c0
// <Person: 0x60400042f860>--0x102d04128--999999999--0x60400042f8a0
從打印信息可以看到,person與personCopy指針不一樣猾警,person的contact對象屬性與personCopy的contact對象屬性指針不一樣孔祸。
而且當(dāng)修改personCopy的contact對象屬性中的phone值時,原對象person的contact對象屬性中的phone值沒有跟著改變发皿,所以是實現(xiàn)了完全復(fù)制崔慧。
如何讓子類對象支持復(fù)制操作?
為了不影響Person類穴墅,新建一個Father類:
// Father.h
@interface Father : NSObject<NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
/// 聯(lián)系方式
@property (nonatomic, strong) Contact *contact;
@end
// Father.m
@implementation Father
- (id)copyWithZone:(NSZone *)zone{
//創(chuàng)建新的對象空間
Father *f = [[self class] allocWithZone:zone];
f.name = self.name;
f.age = self.age;
// 深復(fù)制
f.contact = [self.contact copy];
return f;
}
@end
Father類稍微做了調(diào)整惶室,把繼承NSCopying協(xié)議放在.h文件中,再新建一個Son類繼承Father類:
// Son.h
@interface Son : Father
@property (nonatomic, assign) double height;
@property (nonatomic, assign) double weight;
/// 成績
@property (nonatomic, strong) Score *score;
@end
因為是繼承關(guān)系温自,子類也遵循了NSCopying協(xié)議,但是并沒有重寫copyWithZone:方法皇钞,如果進行復(fù)制操作會是怎樣的結(jié)果呢悼泌?
- (void)viewDidLoad {
[super viewDidLoad];
Son *son = [[Son alloc] init];
son.height = 173.0;
son.weight = 120;
Score *score = [[Score alloc] init];
score.math = 100.0;
score.chinese = 99.0;
score.english = 88;
son.score = score;
Son *sonCopy = [son copy];
NSLog(@"%@--%@", son, son.score); // <Son: 0x604000266c00>--<Score: 0x60400003e5a0>
NSLog(@"%@--%@", sonCopy, sonCopy.score); // <Son: 0x604000266d80>--(null)
}
打印信息顯示,son對象進行了深復(fù)制夹界,但是sonCopy的score對象是空的券躁。為什么呢?因為進行[son copy]操作時掉盅,子類沒有重寫copyWithZone:方法也拜,會去父類那里找,也可以從斷點中看到:
[圖片上傳失敗...(image-a31a9f-1530757237487)]
父類中self指向的是Son類趾痘,那么[[self class] allocWithZone:zone]表示給son創(chuàng)建一個新的空間慢哈,也就是之前打印信息顯示結(jié)果一樣完成了深復(fù)制操作,但是f對象中沒有給score對象屬性賦值永票,所以是null的卵贱。
想要score有值,必然要重寫copyWithZone:方法侣集。
// Son.m
@implementation Son
- (id)copyWithZone:(NSZone *)zone{
Son *s = [[self class] allocWithZone:zone];
s.height = self.height;
s.weight = self.height;
s.score = [self.score copy];
return s;
}
@end
而且Score也要實現(xiàn)深復(fù)制操作:
// Score.m
@implementation Score
- (id)copyWithZone:(NSZone *)zone{
Score *s = [[self class] allocWithZone:zone];
s.math = self.math;
s.chinese = self.chinese;
s.english = self.english;
return s;
}
@end
接下來驗證一下:
- (void)viewDidLoad {
[super viewDidLoad];
Son *son = [[Son alloc] init];
son.height = 173.0;
son.weight = 120;
Score *score = [[Score alloc] init];
score.math = 100.0;
score.chinese = 99.0;
score.english = 88;
son.score = score;
Son *sonCopy = [son copy];
NSLog(@"%@--%@", son, son.score); // <Son: 0x6040002760c0>--<Score: 0x604000429e20>
NSLog(@"%@--%@", sonCopy, sonCopy.score); // <Son: 0x604000276180>--<Score: 0x604000429e00>
}
通過打印信息看到實現(xiàn)深復(fù)制功能键俱。
但是可能有一個疑問?Son是繼承Father類的世分,要是Father類的屬性也有值呢编振?
首先不修改復(fù)制方法的代碼,簡單的給Father類的屬性賦值測試一下先:
- (void)viewDidLoad {
[super viewDidLoad];
Son *son = [[Son alloc] init];
son.height = 173.0;
son.weight = 120;
Score *score = [[Score alloc] init];
score.math = 100.0;
score.chinese = 99.0;
score.english = 88;
son.score = score;
Contact *contact = [[Contact alloc] init];
contact.phone = @"123456789";
contact.email = @"feng@gmail";
son.contact = contact;
son.name = @"daisuke";
Son *sonCopy = [son copy];
NSLog(@"%@--%@--%@--%@", son, son.score, son.name, son.contact);
// <Son: 0x604000273240>--<Score: 0x6040000335e0>--daisuke--<Contact: 0x604000033600>
NSLog(@"%@--%@--%@--%@", sonCopy, sonCopy.score, sonCopy.name, sonCopy.contact);
// <Son: 0x604000273580>--<Score: 0x604000236c00>--(null)--(null)
}
從打印信息看到sonCopy.name和sonCopy.contact兩個值都是null的臭埋,為什么呢踪央?
因為Son類重寫了copyWithZone:方法,自己完成了深復(fù)制操作瓢阴,并沒有考慮到父類也需要深復(fù)制畅蹂。自然而然沒有運行父類的copyWithZone:方法,所以就出現(xiàn)了sonCopy對象的父類屬性值時null的荣恐。
修改如下:
// Son.m
- (id)copyWithZone:(NSZone *)zone{
// 使用super
Son *s = [super copyWithZone:zone];
s.height = self.height;
s.weight = self.height;
s.score = [self.score copy];
return s;
}
測試:
- (void)viewDidLoad {
[super viewDidLoad];
Son *son = [[Son alloc] init];
son.height = 173.0;
son.weight = 120;
Score *score = [[Score alloc] init];
score.math = 100.0;
score.chinese = 99.0;
score.english = 88;
son.score = score;
Contact *contact = [[Contact alloc] init];
contact.phone = @"123456789";
contact.email = @"feng@gmail";
son.contact = contact;
son.name = @"daisuke";
Son *sonCopy = [son copy];
NSLog(@"%@--%@--%@--%@", son, son.score, son.name, son.contact);
// <Son: 0x604000273240>--<Score: 0x6040000335e0>--daisuke--<Contact: 0x604000033600>
NSLog(@"%@--%@--%@--%@", sonCopy, sonCopy.score, sonCopy.name, sonCopy.contact);
// <Son: 0x600000460700>--<Score: 0x6000000301c0>--daisuke--<Contact: 0x6000000301a0>
}
這樣sonCopy.name和 sonCopy.contact兩個屬性都有值了液斜,也完成了深復(fù)制,從斷點也可以看到運行了父類的copyWithZone:方法叠穆,并且把父類的相關(guān)屬性進行了深復(fù)制少漆,如下圖顯示:
[圖片上傳失敗...(image-112dc2-1530757237487)]
互相引用進行深拷貝結(jié)果如何?
在很多多情況下痹束,對象都是互相引用的检疫,當(dāng)然一個是strong一個是weak,否則造成循環(huán)引用祷嘶,引起內(nèi)存泄漏屎媳。比如:信用卡必須有一個人的屬性夺溢,而人未必有信用卡的屬性。那么對信用卡進行深復(fù)制烛谊,結(jié)果是如何呢风响?
第一反應(yīng)想到的是首先創(chuàng)建一個People類并擁有一個(weak引用)card屬性,一個Card類并擁有一個(strong引用)people屬性丹禀。然后遵循NSCopying協(xié)議状勤,實現(xiàn)copyWithZone:方法。
結(jié)果發(fā)現(xiàn)在People類中的copyWithZone:方法中不能對card進行copy操作双泪,因為進行copy的時候新對象的引用計數(shù)器會+1持搜,這樣跟weak引用造成沖突:
// People.m
@implementation People
- (id)copyWithZone:(NSZone *)zone{
People *p = [[self class] allocWithZone:zone];
// 警告:Assigning retained object to weak property; object will be released after assignment
p.card = [self.card copy];
return p;
}
@end
警告信息:Assigning retained object to weak property; object will be released after assignment
因為card屬性是weak類型,不能對它進行copy操作焙矛。否則造成死循環(huán)而崩潰葫盼,如下圖顯示:
[圖片上傳失敗...(image-3133a4-1530757237487)]
那么不能進行copy操作,直接賦值會怎樣呢村斟?
// Card.h
@class People;
@interface Card : NSObject<NSCopying>
@property (nonatomic, copy) NSString *number;
@property (nonatomic, strong) People *people;
@end
// Card.m
@implementation Card
- (id)copyWithZone:(NSZone *)zone{
Card *card = [[self class] allocWithZone:zone];
card.people = [self.people copy];
card.number = self.number;
return card;
}
@end
// People.h
@class Card;
@interface People : NSObject<NSCopying>
@property (nonatomic, weak) Card *card;
@property (nonatomic, copy) NSString *name;
@end
// People.m
@implementation People
- (id)copyWithZone:(NSZone *)zone{
People *p = [[self class] allocWithZone:zone];
p.card = self.card;
p.name = self.name;
return p;
}
@end
測試:
- (void)viewDidLoad {
[super viewDidLoad];
Card *card = [[Card alloc] init];
card.number = @"9999999";
People *people = [[People alloc] init];
people.name = @"daisuke";
card.people = people;
people.card = card;
Card *cardCopy = [card copy];
NSLog(@"%@--%@--%@", card, card.people, card.people.card);
NSLog(@"%@--%@--%@", cardCopy, cardCopy.people, cardCopy.people.card);
// <Card: 0x60000042b700>--<People: 0x6000004297e0>--<Card: 0x60000042b700>
// <Card: 0x60000042b640>--<People: 0x60000042bac0>--<Card: 0x60000042b700>
}
從打印信息看出card.people.card贫导、cardCopy.people.card兩個的地址是一樣的:0x60000042b700,而0x60000042b700指向的是card的地址蟆盹,通過一張圖展示他們的關(guān)系:
[圖片上傳失敗...(image-c0ac9e-1530757237487)]
顯然這個不是我們想要的結(jié)果孩灯,想要的結(jié)果如下圖顯示:
[圖片上傳失敗...(image-dfe9d-1530757237487)]
因為對對象進行深復(fù)制,里面的對象屬性也要深復(fù)制逾滥,但是因為對象屬性是weak引用峰档,不允許copy操作,否則造死成循環(huán)而崩潰匣距,怎么辦呢面哥?
由于能力有限,只能做一下簡單處理毅待,如果哪位有好的建議請聯(lián)系我,謝謝归榕。
// Card.m
@implementation Card
- (id)copyWithZone:(NSZone *)zone{
Card *card = [[self class] allocWithZone:zone];
card.people = [self.people copy];
// 重新指向新創(chuàng)建的card
card.people.card = card;
card.number = self.number;
return card;
}
@end
把people的card屬性重新指向新創(chuàng)建的card尸红,進行測試:
- (void)viewDidLoad {
[super viewDidLoad];
Card *card = [[Card alloc] init];
card.number = @"9999999";
People *people = [[People alloc] init];
people.name = @"daisuke";
card.people = people;
people.card = card;
Card *cardCopy = [card copy];
NSLog(@"%@--%@--%@", card, card.people, card.people.card);
NSLog(@"%@--%@--%@", cardCopy, cardCopy.people, cardCopy.people.card);
// <Card: 0x60400003a9e0>--<People: 0x60400042d120>--<Card: 0x60400003a9e0>
// <Card: 0x60400042c220>--<People: 0x60400042b560>--<Card: 0x60400042c220>
}
顯然這樣就能簡單實現(xiàn)想要的結(jié)果。
基類runtime方式讓所有子類自動實現(xiàn)深復(fù)制操作(有不足之處)
創(chuàng)建基類BaseModel刹泄,有兩種方式實現(xiàn)深復(fù)制:
基類BaseModel遵循NSCopying協(xié)議
// BaseModel.m
- (id)copyWithZone:(NSZone *)zone{
id object = [[self class] allocWithZone:zone];
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
for (int i = 0; i < propertyCount; i++) {
const char *name = property_getName(properties[I]);
NSString *propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSObject<NSCopying> *tempValue = [self valueForKey:propertyName];
if (tempValue) {
// 此處如果是對象屬性外里,且形成閉環(huán)關(guān)系,會造成死循環(huán)導(dǎo)致崩潰特石。
id value = [tempValue copy];
[object setValue:value forKey:propertyName];
}
}
return object;
}
使用runtime方式遍歷遞歸對象的屬性而進行深復(fù)制盅蝗,這里有一個弊端,該方式不適合有閉環(huán)方式的對象使用姆蘸,否則造成死循環(huán)墩莫。上面講的互相引用的例子就是這個的證明芙委,關(guān)系如下圖顯示:
[圖片上傳失敗...(image-7d3ad-1530757237487)]
所以使用BaseModel類方式的話,必須確保對象與對象之間沒有形成閉環(huán)關(guān)閉狂秦。那么就測試一下沒有閉環(huán)關(guān)系例子:
新建FatherModel類灌侣、ContactModel類分別繼承于BaseModel類
// FatherModel.h
@interface FatherModel : BaseModel
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
/// 聯(lián)系方式
@property (nonatomic, strong) ContactModel *contact;
@end
// FatherModel.m
@implementation FatherModel
@end
// ContactModel.h
@interface ContactModel : BaseModel
@property (nonatomic, copy) NSString *phone;
@property (nonatomic, copy) NSString *email;
@end
// ContactModel.m
@implementation ContactModel
@end
測試:
- (void)viewDidLoad {
[super viewDidLoad];
FatherModel *father = [[FatherModel alloc] init];
father.name = @"daisuke";
father.age = 26;
ContactModel *contact = [[ContactModel alloc] init];
contact.phone = @"123456789";
contact.email = @"feng@gmail";
father.contact = contact;
FatherModel *fatherCopy = [father copy];
NSLog(@"%@--%@--%@--%@", father, father.name, father.contact.phone, father.contact);
NSLog(@"%@--%@--%@--%@", fatherCopy, fatherCopy.name,fatherCopy.contact.phone, fatherCopy.contact);
// <FatherModel: 0x604000035b80>--daisuke--123456789--<ContactModel: 0x604000035ba0>
// <FatherModel: 0x604000231960>--daisuke--123456789--<ContactModel: 0x6040002312e0>
}
從打印信息中看到實現(xiàn)了深復(fù)制功能。
注意點:對象屬性如果沒有遵循NSCopying協(xié)議裂问,只是簡單賦值侧啼,不會進行深復(fù)制操作。
基類BaseModel遵循NSCoding協(xié)議
// BaseModel.m
@implementation BaseModel
- (void)encodeWithCoder:(NSCoder *)aCoder{
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
for (int i = 0; i < propertyCount; i++) {
const char *name = property_getName(properties[I]);
NSString *propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSObject *tempValue = [self valueForKey:propertyName];
if (tempValue && [tempValue conformsToProtocol:@protocol(NSCopying)]) {
[aCoder encodeObject:tempValue forKey:propertyName];
}
}
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super init];
if (self) {
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
for (int i = 0; i < propertyCount; i++) {
const char *name = property_getName(properties[I]);
NSString *propertyName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
NSObject *tempValue = [aDecoder decodeObjectForKey:propertyName];
if (tempValue) {
[self setValue:tempValue forKey:propertyName];
}
}
}
return self;
}
@end
測試新建CompanyModel類沒有繼承BaseModel堪簿,也沒有遵循NSCoding協(xié)議痊乾。
FatherModel *father = [[FatherModel alloc] init];
father.name = @"daisuke";
father.age = 26;
CompanyModel *company = [[CompanyModel alloc] init];
company.companyName = @"小公司";
father.company = company;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:father];
FatherModel *fatherCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"%@--%@--%@--%@", father, father.name, father.company, father.company.companyName);
NSLog(@"%@--%@--%@--%@", fatherCopy, fatherCopy.name,fatherCopy.company, fatherCopy.company.companyName);
// <FatherModel: 0x60400024fbd0>--daisuke--<CompanyModel: 0x6040000086b0>--小公司
// <FatherModel: 0x60400024fba0>--daisuke--(null)--(null)
從打印信息看到fatherCopy的company是空的。那么讓CompanyModel類繼承BaseModel椭更,然后直接運行:
FatherModel *father = [[FatherModel alloc] init];
father.name = @"daisuke";
father.age = 26;
CompanyModel *company = [[CompanyModel alloc] init];
company.companyName = @"小公司";
father.company = company;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:father];
FatherModel *fatherCopy = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(@"%@--%@--%@--%@", father, father.name, father.company, father.company.companyName);
NSLog(@"%@--%@--%@--%@", fatherCopy, fatherCopy.name,fatherCopy.company, fatherCopy.company.companyName);
// <FatherModel: 0x60400025a550>--daisuke--<CompanyModel: 0x604000012240>--小公司
// <FatherModel: 0x600000257d30>--daisuke--<CompanyModel: 0x6000000076f0>--小公司
發(fā)現(xiàn)屬性有值了且完成了深復(fù)制操作符喝。
聲明屬性是使用copy、strong的區(qū)別甜孤?
新建一個CopyOrStrongModel類协饲。
copy聲明
// 使用copy聲明name屬性:
@interface CopyOrStrongModel : NSObject
@property (nonatomic, copy) NSString *name;
@end
測試:
- (void)viewDidLoad {
[super viewDidLoad];
CopyOrStrongModel *model = [[CopyOrStrongModel alloc] init];
model.name = @"daisuke";
NSLog(@"model.name是否是NSMutableString類型:%@", @([model.name isKindOfClass:[NSMutableString class]]));
// model.name是否是NSMutableString類型:0
}
把值改為是可變類型的:
- (void)viewDidLoad {
[super viewDidLoad];
CopyOrStrongModel *model = [[CopyOrStrongModel alloc] init];
NSMutableString *stringM = [[NSMutableString alloc] initWithString:@"daisuke"];
model.name = stringM;
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke--0xa656b75736961647--daisuke--0x6040002556c0
[stringM appendString:@"追加"];
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke--0xa656b75736961647--daisuke追加--0x6040002556
NSLog(@"model.name是否是NSMutableString類型:%@", @([model.name isKindOfClass:[NSMutableString class]]));
// model.name是否是NSMutableString類型:0
}
發(fā)現(xiàn)雖然stringM是可變字符串,但是進行model.name = stringM;實際是對可變字符串進行了[可變字符串 copy]操作缴川,從而得到的是NSString類型茉稠。
strong聲明
@property (nonatomic, strong) NSString *name;
測試:
- (void)viewDidLoad {
[super viewDidLoad];
CopyOrStrongModel *model = [[CopyOrStrongModel alloc] init];
model.name = @"daisuke";
NSLog(@"%@--%p", model.name, model.name);
}
使用這種方式跟copy一樣,但是使用可變字符串賦值又會如何呢把夸?
- (void)viewDidLoad {
[super viewDidLoad];
CopyOrStrongModel *model = [[CopyOrStrongModel alloc] init];
NSMutableString *stringM = [[NSMutableString alloc] initWithString:@"daisuke"];
model.name = stringM;
// strong
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke--0xa656b75736961647--daisuke--0x6040002556c0
[stringM appendString:@"追加"];
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke追加--0x60000025d460--daisuke追加--0x60000025d46
NSLog(@"model.name是否是NSMutableString類型:%@", @([model.name isKindOfClass:[NSMutableString class]]));
// model.name是否是NSMutableString類型:1
}
從打印信息的到如果是strong類型聲明的話而线,可變字符串賦值的后 model.name實際類型變成了NSMutableString類型。這不是我們想要的結(jié)果恋日,顯然對于model來說name屬性的類型不想因為外界的賦值而改變膀篮,也不想因為外界的值改變了,model.name的值也跟著改變岂膳。
總結(jié):
- 使用NSString賦值誓竿,使用copy或strong聲明都可以
- 使用NSMutableString賦值,copy聲明不會改變原類型([可變字符串 copy]得到的是NSString類型)谈截,也不會跟隨外界賦的值而改變筷屡。使用strong聲明就會。
重寫Setter方法
當(dāng)然有一種周全的方法簸喂,即便你是copy還是strong聲明毙死,賦值的是NSString還是NSMutableString類型,都不想外界修改的話喻鳄。你可以重寫setName:方法扼倘,例如:
- (void)setName:(NSString *)name{
NSLog(@"%p", name);
_name = [name copy];
NSLog(@"%p", _name);
}
測試:
- 使用copy方式:
- (void)viewDidLoad {
[super viewDidLoad];
CopyOrStrongModel *model = [[CopyOrStrongModel alloc] init];
NSMutableString *stringM = [[NSMutableString alloc] initWithString:@"daisuke"];
model.name = stringM;
// copy
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke--0xa656b75736961647--daisuke--0x6040000564d0
[stringM appendString:@"追加"];
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke--0xa656b75736961647--daisuke追加--0x6040000564d0
NSLog(@"model.name是否是NSMutableString類型:%@", @([model.name isKindOfClass:[NSMutableString class]]));
// model.name是否是NSMutableString類型:0
}
- strong方式:
- (void)viewDidLoad {
[super viewDidLoad];
CopyOrStrongModel *model = [[CopyOrStrongModel alloc] init];
NSMutableString *stringM = [[NSMutableString alloc] initWithString:@"daisuke"];
model.name = stringM;
// strong
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke--0xa656b75736961647--daisuke--0x604000445880
[stringM appendString:@"追加"];
NSLog(@"%@--%p--%@--%p", model.name, model.name, stringM, stringM);
// daisuke--0xa656b75736961647--daisuke追加--0x604000445880
NSLog(@"model.name是否是NSMutableString類型:%@", @([model.name isKindOfClass:[NSMutableString class]]));
// model.name是否是NSMutableString類型:0
}
顯然,重寫了setName:方法并對name進行了copy操作除呵,無論是copy還是strong聲明再菊,賦值的是NSString還是NSMutableString類型爪喘,都不會受到外界影響,所以應(yīng)該養(yǎng)成這個習(xí)慣袄简,這樣就萬無一失了腥放。
可變類型
CopyOrStrongModel聲明兩個屬性:
@property (nonatomic, strong) NSMutableString *mutableString1;
@property (nonatomic, copy) NSMutableString *mutableString2;
測試:
- (void)viewDidLoad {
[super viewDidLoad];
CopyOrStrongModel *model = [[CopyOrStrongModel alloc] init];
NSMutableString *string = @"daisuke".mutableCopy;
model.mutableString1 = string;
model.mutableString2 = string;
NSLog(@"strong聲明的model.mutableString1的類型是否是NSMutableString: %@", @([model.mutableString1 isKindOfClass:[NSMutableString class]]));
// strong聲明的model.mutableString1的類型是否是NSMutableString: 1
NSLog(@"copy聲明的model.mutableString1的類型是否是NSMutableString: %@", @([model.mutableString2 isKindOfClass:[NSMutableString class]]));
// copy聲明的model.mutableString1的類型是否是NSMutableString: 0
}
注意:使用copy聲明NSMutableString類型的屬性,實際得到的是NSString類型
demo
參考文檔
聲明
- 以上內(nèi)容如有侵權(quán)绿语,請聯(lián)系更正
- 轉(zhuǎn)載請注明出處