從NSCopying協(xié)議到copy,mutableCopy

NSCopying 對(duì)應(yīng)copy

想要使某個(gè)類支持拷貝功能,返回不可變對(duì)象,只需要將這個(gè)類遵從NSCopying協(xié)議并實(shí)現(xiàn)其中的方法即可(只有一個(gè)方法)。

///< NSZone:之前再開(kāi)發(fā)程序的時(shí)候,會(huì)據(jù)此把內(nèi)存分成不同的“區(qū)”(zone),而對(duì)象會(huì)創(chuàng)建在某個(gè)區(qū)里面。
///< 現(xiàn)在每個(gè)程序只有一個(gè)區(qū)“默認(rèn)區(qū)(default zone)”,所以盡管必須實(shí)現(xiàn)這個(gè)方法,但是不必?fù)?dān)心zone參數(shù)。
- (id)copyWithZone:(nullable NSZone *)zone;


當(dāng)我們實(shí)現(xiàn)了NSCopying協(xié)議后,通過(guò)類對(duì)象調(diào)用copy方法時(shí),copy方法會(huì)去調(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 對(duì)應(yīng)mutableCopy

與NSCopying協(xié)議類似,此協(xié)議對(duì)應(yīng)可變對(duì)象。

- (id)mutableCopyWithZone:(nullable NSZone *)zone;
無(wú)論當(dāng)前版本是否可變,如果需要獲取其可變版本的拷貝,均應(yīng)調(diào)用mutableCopy方法。若要獲取不可變版本的拷貝,需要通過(guò)copy方法獲取。
[NSMutableArray copy]  => NSArray
[NSArray mutableCopy]  => NSMutableArray

*** 注意:*** 如果自定義對(duì)象分為可變版本和不可變版本,那么需要同時(shí)實(shí)現(xiàn)NSCopying和NSMutableCopying協(xié)議。

在C++中有一種構(gòu)造函數(shù)叫:copy構(gòu)造函數(shù)喲。

copy與mutableCopy一個(gè)實(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
         */
    }
image.png

深copy 與 淺copy

深copy:當(dāng)一個(gè)類擁有資源,當(dāng)這個(gè)類的對(duì)象(資源)發(fā)生復(fù)制的過(guò)程。(copy完后的對(duì)象與被copy對(duì)象相互獨(dú)立,不會(huì)相互影響)
淺copy:復(fù)制過(guò)程中并未復(fù)制資源的情況。

property 中的copy(內(nèi)存語(yǔ)意)

先release舊值,再copy新值。復(fù)制一個(gè)對(duì)象并創(chuàng)建strong關(guān)聯(lián)。copy的本質(zhì)為復(fù)制該內(nèi)存所存儲(chǔ)的內(nèi)容,重新創(chuàng)建一個(gè)對(duì)象賦給其相同的內(nèi)容。
通俗點(diǎn):copy一個(gè)新對(duì)象,新對(duì)象引用計(jì)數(shù)+1,原對(duì)象不變。

在NSString,NSArray,NSDictionary等 經(jīng)常使用copy關(guān)鍵字,why?(這個(gè)問(wèn)題被說(shuō)爛了吧)

由于他們都對(duì)應(yīng)有Mutable版本。
在實(shí)際使用時(shí),父類的指針可以指向?qū)?yīng)子類的對(duì)象,我們使用copy的牧師是為了讓本對(duì)象的屬性不受外界影響,使用copy無(wú)論給我傳入的是一個(gè)可變對(duì)象還是不可變對(duì)象,我本身持有的就是一個(gè)不可變的副本。
如果我們使用strong,那么這個(gè)屬性就有可能指向一個(gè)可變對(duì)象,如果這個(gè)可變對(duì)象在外部被修改,那么會(huì)影響該屬性值。
相關(guān)測(cè)試:

{
        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
         ///< 下面就會(huì)出問(wèn)題了
         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)閷?duì)于block使用copy或者strong都是可以的。因?yàn)榫幾g器自動(dòng)對(duì)block進(jìn)行了copy操作。在開(kāi)發(fā)中寫上copy關(guān)鍵字 是一個(gè)良好的習(xí)慣。時(shí)刻提醒自己,編譯器會(huì)對(duì)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.

Apple相關(guān)文檔1 copy
Apple相關(guān)文檔2 block

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容