从NSCopying协议到copy,mutableCopy

NSCopying 对应copy

想要使某个类支持拷贝功能,返回不可变对象,只需要将这个类遵从NSCopying协议并实现其中的方法即可(只有一个方法)。

///< NSZone:之前再开发程序的时候,会据此把内存分成不同的“区”(zone),而对象会创建在某个区里面。
///< 现在每个程序只有一个区“默认区(default zone)”,所以尽管必须实现这个方法,但是不必担心zone参数。
- (id)copyWithZone:(nullable NSZone *)zone;


当我们实现了NSCopying协议后,通过类对象调用copy方法时,copy方法会去调用copyWithZone方法。
注意:Foundation框架中的所有collection类在默认情况下都执行的是浅拷贝。

@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],因为指针可能实际指向的是A的子类。
    A *copyA = [[[self class] allocWithZone:zone] initWithName:_name];
    return copyA;
}

@end

NSMutableCopying 对应mutableCopy

与NSCopying协议类似,此协议对应可变对象。

- (id)mutableCopyWithZone:(nullable NSZone *)zone;
无论当前版本是否可变,如果需要获取其可变版本的拷贝,均应调用mutableCopy方法。若要获取不可变版本的拷贝,需要通过copy方法获取。
[NSMutableArray copy]  => NSArray
[NSArray mutableCopy]  => NSMutableArray

*** 注意:*** 如果自定义对象分为可变版本和不可变版本,那么需要同时实现NSCopying和NSMutableCopying协议。

在C++中有一种构造函数叫:copy构造函数哟。

copy与mutableCopy一个实例

{
        ///< 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:当一个类拥有资源,当这个类的对象(资源)发生复制的过程。(copy完后的对象与被copy对象相互独立,不会相互影响)
浅copy:复制过程中并未复制资源的情况。

property 中的copy(内存语意)

先release旧值,再copy新值。复制一个对象并创建strong关联。copy的本质为复制该内存所存储的内容,重新创建一个对象赋给其相同的内容。
通俗点:copy一个新对象,新对象引用计数+1,原对象不变。

在NSString,NSArray,NSDictionary等 经常使用copy关键字,why?(这个问题被说烂了吧)

由于他们都对应有Mutable版本。
在实际使用时,父类的指针可以指向对应子类的对象,我们使用copy的牧师是为了让本对象的属性不受外界影响,使用copy无论给我传入的是一个可变对象还是不可变对象,我本身持有的就是一个不可变的副本。
如果我们使用strong,那么这个属性就有可能指向一个可变对象,如果这个可变对象在外部被修改,那么会影响该属性值。
相关测试:

{
        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关键字

方法内:block是在栈区的。使用copy可以把block放到堆区上。
在arc中可以不写。因为对于block使用copy或者strong都是可以的。因为编译器自动对block进行了copy操作。在开发中写上copy关键字 是一个良好的习惯。时刻提醒自己,编译器会对block做copy操作。
Apple相关文档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相关文档1 copy
Apple相关文档2 block

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容