先說結果下面會一一測試
1.NSString、NSMutableString、NSArray、NSMutableArray ->copy與mutablecopy
? ? //1不可變字符串
//? ? NSMutableString *string = [NSMutableString stringWithString:@"test"];
//? ? NSString *str = [NSString stringWithString:string];
//? ? NSString *str2 = [str copy];
//? ? NSMutableString *str3 = [str mutableCopy];
//? ? NSLog(@"原始值 --%p---%@--",str,str);
//? ? NSLog(@"copy值 --%p---%@--",str2,str2);
//? ? NSLog(@"mutableCopy值 --%p---%@--",str3,str3);
//? ? [string appendString:@"aaa"];
//? ? [str3 appendString:@"bbbbb"];
//? ? NSLog(@"原始值 --%p---%@--",str,str);
//? ? NSLog(@"copy值 --%p---%@--",str2,str2);
//? ? NSLog(@"mutableCopy值 --%p---%@--",str3,str3);
? ? /*
?? ? 2019-11-11 14:43:35.696 YJApiRequestTool[14269:251156] 原始值 --0xa000000747365744---test--
?? ? 2019-11-11 14:43:35.696 YJApiRequestTool[14269:251156] copy值 --0xa000000747365744---test--
?? ? 2019-11-11 14:43:35.696 YJApiRequestTool[14269:251156] mutableCopy值 --0x7fcd34c0d770---test--
?? ? 2019-11-11 14:43:35.696 YJApiRequestTool[14269:251156] 原始值 --0xa000000747365744---test--
?? ? 2019-11-11 14:43:35.696 YJApiRequestTool[14269:251156] copy值 --0xa000000747365744---test--
?? ? 2019-11-11 14:43:35.697 YJApiRequestTool[14269:251156] mutableCopy值 --0x7fcd34c0d770---testbbbbb--
?? ? 原始值與copy出來的值一樣 一直沒有改變 地址也一樣
?? ? mutableCopy 地址不一樣 值改變不影響原始值
?? ? */
? ? //2 可變字符串
//? ? NSMutableString * mutableStr = [[NSMutableString alloc]initWithString:string];
//? ? NSString*mutableStr2 = [mutableStr copy];
//? ? NSMutableString*mutableStr3 = [mutableStr mutableCopy];
//? ? ? ? NSLog(@"原始值 --%p---%@--",mutableStr,mutableStr);
//? ? ? ? NSLog(@"copy值 --%p---%@--",mutableStr2,mutableStr2);
//? ? ? ? NSLog(@"mutableCopy值 --%p---%@--",mutableStr3,mutableStr3);
//? ? ? ? [string appendString:@"aaa"];
//? ? ? ? [mutableStr appendString:@"bbbbb"];
//? ? ? ? [mutableStr3 appendString:@"ccccccc"];
//? ? ? ? NSLog(@"原始值 --%p---%@--",mutableStr,mutableStr);
//? ? ? ? NSLog(@"copy值 --%p---%@--",mutableStr2,mutableStr2);
//? ? ? ? NSLog(@"mutableCopy值 --%p---%@--",mutableStr3,mutableStr3);
? ? /*
?? ? 2019-11-11 14:58:27.749 YJApiRequestTool[14636:263259] 原始值 --0x7fee622381d0---test--
?? ? 2019-11-11 14:58:27.750 YJApiRequestTool[14636:263259] copy值 --0xa000000747365744---test--
?? ? 2019-11-11 14:58:27.750 YJApiRequestTool[14636:263259] mutableCopy值 --0x7fee6223a0d0---test--
?? ? 2019-11-11 14:58:27.750 YJApiRequestTool[14636:263259] 原始值 --0x7fee622381d0---testbbbbb--
?? ? 2019-11-11 14:58:27.750 YJApiRequestTool[14636:263259] copy值 --0xa000000747365744---test--
?? ? 2019-11-11 14:58:27.751 YJApiRequestTool[14636:263259] mutableCopy值 --0x7fee6223a0d0---testccccccc--
?? ? 可變字符串,copy,mutableCopy 都會將整個對象重新拷貝
?? ? */
//? ? NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
//? ? NSArray *originArr = [NSArray arrayWithObject:array];
//? ? NSArray *copyArr = [originArr copy];
//? ? NSMutableArray *mutableArr = [originArr mutableCopy];
//
//? ? NSLog(@"原始數(shù)組%p? ? %@",originArr,originArr);
//? ? NSLog(@"Copy數(shù)組%p? ? %@",copyArr,copyArr);
//? ? NSLog(@"MutableCopy數(shù)組%p? ? %@",mutableArr,mutableArr);
//
//? ? [array addObject:@"3"];
//? ? [mutableArr addObject:@"4"];
//
//? ? NSLog(@"修改數(shù)組%p? ? %@",originArr,originArr);
//? ? NSLog(@"修改Copy數(shù)組%p? ? %@",copyArr,copyArr);
//? ? NSLog(@"修改MutableCopy數(shù)組%p? ? %@",mutableArr,mutableArr);
? ? /*
?? ? 2019-11-11 15:19:42.079 YJApiRequestTool[15114:277864] 原始數(shù)組0x7faba8507530? ? (
?? ? (
?? ? 1,
?? ? 2
?? ? )
?? ? )
?? ? 2019-11-11 15:19:42.080 YJApiRequestTool[15114:277864] Copy數(shù)組0x7faba8507530? ? (
?? ? (
?? ? 1,
?? ? 2
?? ? )
?? ? )
?? ? 2019-11-11 15:19:42.080 YJApiRequestTool[15114:277864] MutableCopy數(shù)組0x7faba850ad00? ? (
?? ? (
?? ? 1,
?? ? 2
?? ? )
?? ? )
?? ? 2019-11-11 15:19:42.080 YJApiRequestTool[15114:277864] 修改數(shù)組0x7faba8507530? ? (
?? ? (
?? ? 1,
?? ? 2,
?? ? 3
?? ? )
?? ? )
?? ? 2019-11-11 15:19:42.080 YJApiRequestTool[15114:277864] 修改Copy數(shù)組0x7faba8507530? ? (
?? ? (
?? ? 1,
?? ? 2,
?? ? 3
?? ? )
?? ? )
?? ? 2019-11-11 15:19:42.080 YJApiRequestTool[15114:277864] 修改MutableCopy數(shù)組0x7faba850ad00? ? (
?? ? (
?? ? 1,
?? ? 2,
?? ? 3
?? ? ),
?? ? 4
?? ? )
?? ? copy 指針拷貝 指向的是同一塊內存地址
?? ? MutableCopy 是對象拷貝一份 修改值后 不影響之前的值
?? ? 這里或許有個疑問 originArr 為什么會是[(1,2,3)];這個其實很容易理解的 那是因為 指針指向的這塊內存區(qū)域值發(fā)生了改變 所以才是[(1,2,3)]
?? ? 因此: 數(shù)組復制,其元素對象始終是指針復制,元素指向的值改變,數(shù)組自然都會改變。
?? ? */
//? ? ? ? NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
//? ? ? ? NSMutableArray *originArr = [NSMutableArray arrayWithArray:array];
//? ? ? ? NSArray *copyArr = [originArr copy];
//? ? ? ? NSMutableArray *mutableArr = [originArr mutableCopy];
//
//? ? ? ? NSLog(@"原始數(shù)組%p? ? %@",originArr,originArr);
//? ? ? ? NSLog(@"Copy數(shù)組%p? ? %@",copyArr,copyArr);
//? ? ? ? NSLog(@"MutableCopy數(shù)組%p? ? %@",mutableArr,mutableArr);
//
//? ? ? ? [array addObject:@"3"];
//? ? ? ? [originArr addObject:@"5"];
//? ? ? ? [mutableArr addObject:@"4"];
//
//? ? ? ? NSLog(@"修改數(shù)組%p? ? %@",originArr,originArr);
//? ? ? ? NSLog(@"修改Copy數(shù)組%p? ? %@",copyArr,copyArr);
//? ? ? ? NSLog(@"修改MutableCopy數(shù)組%p? ? %@",mutableArr,mutableArr);
? ? /*
?? ? 2019-11-11 15:26:05.199 YJApiRequestTool[15278:283199] 原始數(shù)組0x7fbcdd61b7c0? ? (
?? ? 1,
?? ? 2
?? ? )
?? ? 2019-11-11 15:26:05.200 YJApiRequestTool[15278:283199] Copy數(shù)組0x7fbcdd61e090? ? (
?? ? 1,
?? ? 2
?? ? )
?? ? 2019-11-11 15:26:05.200 YJApiRequestTool[15278:283199] MutableCopy數(shù)組0x7fbcdd61d480? ? (
?? ? 1,
?? ? 2
?? ? )
?? ? 2019-11-11 15:26:05.200 YJApiRequestTool[15278:283199] 修改數(shù)組0x7fbcdd61b7c0? ? (
?? ? 1,
?? ? 2,
?? ? 5
?? ? )
?? ? 2019-11-11 15:26:05.200 YJApiRequestTool[15278:283199] 修改Copy數(shù)組0x7fbcdd61e090? ? (
?? ? 1,
?? ? 2
?? ? )
?? ? 2019-11-11 15:26:05.201 YJApiRequestTool[15278:283199] 修改MutableCopy數(shù)組0x7fbcdd61d480? ? (
?? ? 1,
?? ? 2,
?? ? 4
?? ? )
?? ? 對于可變數(shù)組,可以看到不管哪種copy,都會對對象重新拷貝 改變各自的值 互不影響
?? ? */
2、自定義對象的復制
使用copy和mutableCopy復制對象的副本使用起來確實方便,那么我們自定義的類是否可調用copy與mutableCopy方法來復制副本呢?先定義一個Person類,代碼如下:
@interfacePerson:NSObject
@property(nonatomic,assign)NSIntegerage;
@property(nonatomic,copy)NSString *name;
@end
然后嘗試調用Person的copy方法來復制一個副本:
Person *person1=[[Personalloc]init];//創(chuàng)建一個Person對象
person1.age=20;
person1.name=@"張三";
Person *person2=[person1copy];//復制副本
運行程序,將會發(fā)生崩潰,并輸出以下錯誤信息:
[PersoncopyWithZone:]:unrecognized selector senttoinstance0x608000030920
上面的提示:Person找不到copyWithZone:方法。我們將復制副本的代碼換成如下:
Person *person2=[person1mutableCopy];//復制副本
再次運行程序,程序同樣崩潰了,并輸出去以下錯誤信息:
[PersonmutableCopyWithZone:]:unrecognized selector senttoinstance0x600000221120
上面的提示:Person找不到mutableCopyWithZone:方法。
大家可能會覺得疑惑,程序只是調用了copy和mutableCopy方法,為什么會提示找不到copyWithZone:與mutableCopyWithZone:方法呢?其實當程序調用對象的copy方法來復制自身時,底層需要調用copyWithZone:方法來完成實際的復制工作,copy返回實際上就是copyWithZone:方法的返回值;mutableCopy與mutableCopyWithZone:方法也是同樣的道理。
那么怎么做才能讓自定義的對象進行copy與mutableCopy呢?需要做以下事情:
1.讓類實現(xiàn)NSCopying/NSMutableCopying協(xié)議。
2.讓類實現(xiàn)copyWithZone:/mutableCopyWithZone:方法
所以讓我們的Person類能夠復制自身,我們需要讓Person實現(xiàn)NSCopying協(xié)議;然后實現(xiàn)copyWithZone:方法:
@interfacePerson:NSObject
@property(nonatomic,assign)NSIntegerage;
@property(nonatomic,copy)NSString *name;
@end
#import "Person.h"
@implementationPerson
-(id)copyWithZone:(NSZone *)zone{
Person *person=[[[selfclass]allocWithZone:zone]init];
person.age=self.age;
person.name=self.name;
returnperson;
}
@end
運行之后發(fā)現(xiàn)我們實現(xiàn)了對象的復制:
同時需要注意的是如果對象中有其他指針類型的實例變量,且只是簡單的賦值操作:person.obj2 = self.obj2,其中obj2是另一個自定義類,如果我們修改obj2中的屬性,我們會發(fā)現(xiàn)復制后的person對象中obj2對象中的屬性值也變了,因為對于這個對象并沒有進行copy操作,這樣的復制操作不是完全的復制,如果要實現(xiàn)完全的復制,需要將obj2對應的類也要實現(xiàn)copy,然后這樣賦值:person.obj2 = [self.obj2 copy]。如果對象很多或者層級很多,實現(xiàn)起來還是很麻煩的。如果需要實現(xiàn)完全復制同樣還有另有一種方法,那就是歸檔:
Person *person2=[NSKeyedUnarchiverunarchiveObjectWithData:[NSKeyedArchiverarchivedDataWithRootObject:person1]];
這樣我們就實現(xiàn)了自定義對象的復制,需要指出的是如果重寫copyWithZone:方法時,其父類已經實現(xiàn)NSCopying協(xié)議,并重寫過了copyWithZone:方法,那么子類重寫copyWithZone:方法應先調用父類的copy方法復制從父類繼承得到的成員變量,然后對子類中定義的成員變量進行賦值:
-(id)copyWithZone:(NSZone *)zone{
idobj=[supercopyWithZone:zone];
//對子類定義的成員變量賦值
...
returnobj;
}
關于mutableCopy的實現(xiàn)與copy的實現(xiàn)類似,只是實現(xiàn)的是NSMutableCopying協(xié)議與mutableCopyWithZone:方法。對于自定義的對象,在我看來并沒有什么可變不可變的概念,因此實現(xiàn)mutableCopy其實是沒有什么意義的,在此就不詳細介紹了。
3、定義屬性的copy指示符
如下段代碼,我們在定義屬性的時候使用了copy指示符:
#import
@interfacePerson:NSObject
@property(nonatomic,copy)NSMutableString *name;
@end
使用如下代碼來進行測試:
Person *person1=[[Personalloc]init];//創(chuàng)建一個Person對象
person1.name=[NSMutableStringstringWithString:@"蘇小妖"];
[person1.nameappendString:@"123"];
運行程序會崩潰,并且提示以下信息:
***Terminating app duetouncaughtexception'NSInvalidArgumentException',reason:'Attempt to mutate immutable object with appendString:'
這段錯誤提示不允許修改person的name屬性,這是因為程序定義name屬性時使用了copy指示符,該指示符置頂調用setName:方法時(通過點語法賦值時,實際上是調用對應的setter方法),程序實際上會使用參數(shù)的副本對name實際變量復制。也就是說,setName:方法的代碼如下:
-(void)setName:(NSMutableString *)name{
_name=[namecopy];
}
copy方法默認是復制該對象的不可變副本,雖然程序傳入的NSMutableString,但程序調用該參數(shù)的copy方法得到的是不可變副本。因此,程序賦給Person對象的name實例變量的值依然是不可變字符串。
注意:定義合成getter、setter方法時并沒有提供mutableCopy指示符。因此即使定義實例變量時使用了可變類型,但只要使用copy指示符,實例變量實際得到的值總是不可變對象。
copy與mutableCopy還有一個特點:
修改源對象的屬性和行為,不會影響副本對象
修改副本對象的屬性和行為,不會影響源對象
互不影響
4.block-copy
對于block為什么用copy 你應該就理解了 這里有一個知識點 block默認保存在棧區(qū)
?? ? 棧區(qū)在對外部對象進行操作時,不會對對象進行retain,當block保存在堆區(qū)時,在外部對象進行操作時,會對對象進行retain。而我們本是是不知道什么時候什么時候調用block的,當block中的對象提前釋放,會造成Crash,但這時又回出現(xiàn)循環(huán)引用又該怎么辦
?? ? __weak typeof(self)weakself = self; 即可解決
?? ? 使用__block 來修飾 這會把 棧中的內存地址放到了堆中 如果不使用__block
?? ? 他會出現(xiàn)倆個指針地址
/*
?NSMutableString *mutableStr = [NSMutableString stringWithString:@"1"];
? ? NSLog(@"1--棧中str的地址%p,str指向堆中的地址%p",&mutableStr,mutableStr);
? ? void(^mutableStrBlock)(void) = ^(void){
? ? ? ? mutableStr.string=@"2";
? ? ? ? NSLog(@"2--棧中str的地址%p,str指向堆中的地址%p",&mutableStr,mutableStr);
? ? }; ? ?mutableStrBlock();
*/