NSCopying 對應(yīng)copy
想要使某個類支持拷貝功能牙勘,返回不可變對象论泛,只需要將這個類遵從NSCopying協(xié)議并實(shí)現(xiàn)其中的方法即可(只有一個方法)揩尸。
///< NSZone:之前再開發(fā)程序的時候,會據(jù)此把內(nèi)存分成不同的“區(qū)”(zone)屁奏,而對象會創(chuàng)建在某個區(qū)里面岩榆。
///< 現(xiàn)在每個程序只有一個區(qū)“默認(rèn)區(qū)(default zone)”,所以盡管必須實(shí)現(xiàn)這個方法坟瓢,但是不必?fù)?dān)心zone參數(shù)勇边。
- (id)copyWithZone:(nullable NSZone *)zone;
當(dāng)我們實(shí)現(xiàn)了NSCopying協(xié)議后,通過類對象調(diào)用copy方法時折联,copy方法會去調(diào)用copyWithZone方法粒褒。
注意:Foundation框架中的所有collection類在默認(rèn)情況下都執(zhí)行的是淺拷貝。
@interface A : NSObject <NSCopying>
@property (nonatomic, copy, readyonly) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
@interface A ()
@property (nonatomic, copy, readywrite) NSString *name;
@end
@implementation A
- (id)copyWithZone:(nullable NSZone *)zone {
///< 這里注意:一定要使用[self class]诚镰,因?yàn)橹羔樋赡軐?shí)際指向的是A的子類奕坟。
A *copyA = [[[self class] allocWithZone:zone] initWithName:_name];
return copyA;
}
@end
NSMutableCopying 對應(yīng)mutableCopy
與NSCopying協(xié)議類似,此協(xié)議對應(yīng)可變對象清笨。
- (id)mutableCopyWithZone:(nullable NSZone *)zone;
無論當(dāng)前版本是否可變月杉,如果需要獲取其可變版本的拷貝,均應(yīng)調(diào)用mutableCopy方法抠艾。若要獲取不可變版本的拷貝苛萎,需要通過copy方法獲取。
[NSMutableArray copy] => NSArray
[NSArray mutableCopy] => NSMutableArray
*** 注意:*** 如果自定義對象分為可變版本和不可變版本检号,那么需要同時實(shí)現(xiàn)NSCopying和NSMutableCopying協(xié)議腌歉。
在C++中有一種構(gòu)造函數(shù)叫:copy構(gòu)造函數(shù)喲。
copy與mutableCopy一個實(shí)例
{
///< copy 淺copy
NSArray *testArray = [NSArray arrayWithObjects:@"a", nil];
id copyArray = [testArray copy];
NSLog(@"class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyArray class], testArray, copyArray, [testArray retainCount], [copyArray retainCount]);
/*
2017-02-23 10:30:04.666 test[75709:577058] class:__NSSingleObjectArrayI addr:0x61000000d6b0, 0x61000000d6b0, retainCount:2, 2
*/
}
{
///< mutableCopy 深copy
NSArray *testArray = [NSArray arrayWithObjects:@"a", nil];
id copyArray = [testArray mutableCopy];
NSLog(@"class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyArray class], testArray, copyArray, [testArray retainCount], [copyArray retainCount]);
/*
2017-02-23 10:30:04.667 test[75709:577058] class:__NSArrayM addr:0x61000000d6e0, 0x610000055270, retainCount:1, 1
*/
}
{
///< copy 深copy
NSMutableArray *testArray = [NSMutableArray arrayWithObjects:@"a", nil];
id copyArray = [testArray copy];
NSLog(@"class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyArray class], testArray, copyArray, [testArray retainCount], [copyArray retainCount]);
/*
2017-02-23 10:33:20.071 test[75737:579793] class:__NSSingleObjectArrayI addr:0x61000005cce0, 0x610000009de0, retainCount:1, 1
*/
}
{
///< mutableCopy 深copy
NSMutableArray *testArray = [NSMutableArray arrayWithObjects:@"a", nil];
id copyArray = [testArray mutableCopy];
NSLog(@"class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyArray class], testArray, copyArray, [testArray retainCount], [copyArray retainCount]);
/*
2017-02-23 10:34:48.538 test[75753:581049] class:__NSArrayM addr:0x61000005ef30, 0x61000005f290, retainCount:1, 1
*/
}
深copy 與 淺copy
深copy:當(dāng)一個類擁有資源齐苛,當(dāng)這個類的對象(資源)發(fā)生復(fù)制的過程翘盖。(copy完后的對象與被copy對象相互獨(dú)立,不會相互影響)
淺copy:復(fù)制過程中并未復(fù)制資源的情況脸狸。
property 中的copy(內(nèi)存語意)
先release舊值最仑,再copy新值藐俺。復(fù)制一個對象并創(chuàng)建strong關(guān)聯(lián)炊甲。copy的本質(zhì)為復(fù)制該內(nèi)存所存儲的內(nèi)容,重新創(chuàng)建一個對象賦給其相同的內(nèi)容欲芹。
通俗點(diǎn):copy一個新對象卿啡,新對象引用計(jì)數(shù)+1,原對象不變菱父。
在NSString颈娜,NSArray剑逃,NSDictionary等 經(jīng)常使用copy關(guān)鍵字,why官辽?(這個問題被說爛了吧)
由于他們都對應(yīng)有Mutable版本蛹磺。
在實(shí)際使用時,父類的指針可以指向?qū)?yīng)子類的對象同仆,我們使用copy的牧師是為了讓本對象的屬性不受外界影響萤捆,使用copy無論給我傳入的是一個可變對象還是不可變對象,我本身持有的就是一個不可變的副本俗批。
如果我們使用strong俗或,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改岁忘,那么會影響該屬性值辛慰。
相關(guān)測試:
{
NSMutableArray *testArray1 = [NSMutableArray arrayWithObjects:@"a", nil];
NSMutableArray *testArray2 = [NSMutableArray arrayWithObjects:@"a", nil];
NSMutableArray *testArray3 = [NSMutableArray arrayWithObjects:@"a", nil];
NSMutableArray *testArray4 = [NSMutableArray arrayWithObjects:@"a", nil];
NSArray *testArray5 = [[[NSArray alloc] initWithObjects:@"a", nil] autorelease];
NSArray *testArray6 = [[[NSArray alloc] initWithObjects:@"a", nil] autorelease];
NSArray *testArray7 = [[[NSArray alloc] initWithObjects:@"a", nil] autorelease];
NSArray *testArray8 = [[[NSArray alloc] initWithObjects:@"a", nil] autorelease];
YLPCopyTest *copyTest = [[[YLPCopyTest alloc] init] autorelease];
// copyTest.testCopyArray = testArray1;
// copyTest.testStrongArray = testArray2;
// copyTest.testCopyMutableArray = testArray3;
// copyTest.testStrongMutableArray = testArray4;
// NSLog(@"testCopyArray NSMutableArray testArray1 copy class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testCopyArray class], copyTest.testCopyArray, testArray1, [copyTest.testCopyArray retainCount], [testArray1 retainCount]);
// NSLog(@"testStrongArray NSMutableArray testArray2 strong class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testStrongArray class], copyTest.testStrongArray, testArray2, [copyTest.testStrongArray retainCount], [testArray2 retainCount]);
// NSLog(@"testCopyMutableArray NSMutableArray testArray3 copy class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testCopyMutableArray class], copyTest.testCopyMutableArray, testArray3, [copyTest.testCopyMutableArray retainCount], [testArray3 retainCount]);
// NSLog(@"testStrongMutableArray NSMutableArray testArray4 copy class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testStrongMutableArray class], copyTest.testStrongMutableArray, testArray4, [copyTest.testStrongMutableArray retainCount], [testArray4 retainCount]);
// /*
// 2017-02-23 11:28:47.455 test[76488:627094] testCopyArray NSMutableArray testArray1 copy class:__NSSingleObjectArrayI addr:0x618000006350, 0x61800005d8e0, retainCount:1, 1
// 2017-02-23 11:28:47.456 test[76488:627094] testStrongArray NSMutableArray testArray2 strong class:__NSArrayM addr:0x61800005da60, 0x61800005da60, retainCount:2, 2
// 2017-02-23 11:28:47.456 test[76488:627094] testCopyMutableArray NSMutableArray testArray3 copy class:__NSSingleObjectArrayI addr:0x618000006320, 0x61800005dc40, retainCount:1, 1
// 2017-02-23 11:28:47.456 test[76488:627094] testStrongMutableArray NSMutableArray testArray4 copy class:__NSArrayM addr:0x61800005dc10, 0x61800005dc10, retainCount:2, 2
// */
copyTest.testCopyArray = testArray5;
copyTest.testStrongArray = testArray6;
copyTest.testCopyMutableArray = testArray7;
copyTest.testStrongMutableArray = testArray8;
NSLog(@"testCopyArray NSArray testArray5 strong class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testCopyArray class], copyTest.testCopyArray, testArray5, [copyTest.testCopyArray retainCount], [testArray5 retainCount]);
NSLog(@"testStrongArray NSArray testArray6 copy class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testStrongArray class], copyTest.testStrongArray, testArray6, [copyTest.testStrongArray retainCount], [testArray6 retainCount]);
NSLog(@"testCopyMutableArray NSArray testArray7 copy class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testCopyMutableArray class], copyTest.testCopyMutableArray, testArray7, [copyTest.testCopyMutableArray retainCount], [testArray7 retainCount]);
NSLog(@"testStrongMutableArray NSArray testArray8 copy class:%@ addr:%p, %p, retainCount:%ld, %ld", [copyTest.testStrongMutableArray class], copyTest.testStrongMutableArray, testArray8, [copyTest.testStrongMutableArray retainCount], [testArray8 retainCount]);
/*
2017-02-23 11:30:11.590 test[76507:628350] testCopyArray NSArray testArray5 strong class:__NSSingleObjectArrayI addr:0x600000010cd0, 0x600000010cd0, retainCount:2, 2
2017-02-23 11:30:11.591 test[76507:628350] testStrongArray NSArray testArray6 copy class:__NSSingleObjectArrayI addr:0x600000010d20, 0x600000010d20, retainCount:2, 2
///< 下面就會出問題了
2017-02-23 11:30:11.591 test[76507:628350] testCopyMutableArray NSArray testArray7 copy class:__NSSingleObjectArrayI addr:0x600000010d30, 0x600000010d30, retainCount:2, 2
2017-02-23 11:30:11.591 test[76507:628350] testStrongMutableArray NSArray testArray8 copy class:__NSSingleObjectArrayI addr:0x600000010d40, 0x600000010d40, retainCount:2, 2
*/
}
block使用copy關(guān)鍵字
方法內(nèi):block是在棧區(qū)的。使用copy可以把block放到堆區(qū)上干像。
在arc中可以不寫帅腌。因?yàn)閷τ赽lock使用copy或者strong都是可以的。因?yàn)榫幾g器自動對block進(jìn)行了copy操作蝠筑。在開發(fā)中寫上copy關(guān)鍵字 是一個良好的習(xí)慣狞膘。時刻提醒自己,編譯器會對block做copy操作什乙。
在Apple相關(guān)文檔2 block中也有寫:
Note: You should specify copy as the property attribute, because a block needs to be copied to keep track of its captured state outside of the original scope. This isn’t something you need to worry about when using Automatic Reference Counting, as it will happen automatically, but it’s best practice for the property attribute to show the resultant behavior. For more information, see Blocks Programming Topics.