属性修饰词Copy的那些坑

引言

OC里有一个Protocol叫做NSCopying,它声明了一个必须要实现的方法- (id)copyWithZone:(nullable NSZone *)zone;
假设有一个Person类,如果要实现这个协议的话,我们一般是这么写的。

@interface Person: NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@end

@implementation Person

- (id)copyWithZone:(nullable NSZone *)zone {
    Person *copy = [[Person allocWithZone:zone] init];
    copy->_name = _name.copy;
    return copy;
}

@end

在方法里我们创建了一个Person对象,被将当前的_name作为实例变量传给了这个新建的对象,最后将这个对象返回,最后这样就实现了copy。

Copy作为属性修饰词的那些坑

  • 被修饰的属性必须要实现NSCopying,否则crash。
  • Copy不一定是Copy
  • Foundation的immutable类如NSArray,NSString在使用Copy时等同于retain或strong。

先看下面的代码

NSString *str1 = @"string";
NSString *str1Copy = str1.copy;
NSLog(@"str1:%p, str2:%p", str1, str1Copy);
        // str1:0x100001058, str2:0x100001058
        
        NSMutableString *mStr1 = [[NSMutableString alloc] initWithString:str1];
        NSMutableString *mStr1Copy = mStr1.copy;
        NSLog(@"\nmStr1 class = %@, address = %p\nmStr1Copy class = %@, address = %p", mStr1.class, mStr1, mStr1Copy.class, mStr1Copy);
        // mStr1     class = __NSCFString,          address = 0x100500230
        // mStr1Copy class = NSTaggedPointerString, address = 0x676e6972747365

        @try {
            [mStr1Copy appendString:@"a"];
        } @catch (NSException *exception) {
            NSLog(@"%@", exception);
            // -[NSTaggedPointerString appendString:]: unrecognized selector sent to instance 0x676e6972747365
        }
        
        NSArray *arr = @[@1, @2, @3];
        NSArray *arrCopy = arr.copy;
        NSLog(@"arr:%p, arrCopy:%p", arr, arrCopy);
        // arr:0x100202dd0, arrCopy:0x100202dd0
        
        NSMutableArray *mArr = [NSMutableArray arrayWithArray:arr];
        NSMutableArray *mArrCopy = mArr.copy;
        NSLog(@"\nmArr class = %@, address = %p\nmArrCopy class = %@, address = %p", mArr.class, mArr, mArrCopy.class, mArrCopy);
        // mArr     class = __NSArrayM, address = 0x100200510
        // mArrCopy class = __NSArrayI, address = 0x100202270

Foundation的NSArray,NSString和它们对应的mutable在使用copy时是与我们期待的行为不同的。

  • immutable对象因为是不可变的,我们需要copy一个对象是因为需要对这个copy进行一定的改变,例如我copy一个文件,然后修改这个文件,保持源文件不变。既然对象本身不可变的,即使你copy出来也是不能改的,那么为什么还需要在内存里再塞一个一模一样的不可变对象呢?
  • mutable对象在实现时也不是我们期待的copy行为,虽然这次的确创建了一个全新的对象,与immutable不同。但是这个新的对象除了在内容上和原来的对象一样外,但是它们的class却是不同的。mutable对象在实现copy方法时实际上是创建了一个immutable对象并返回。

上面两点印证了,Copy不一定是Copy。值得注意的是,因为dynamic typing的关系,对象实际的类型是在运行时决定而不是编译时决定的。所以NSMutableArray *mArrCopy = mArr.copy;是完全没有问题的,你甚至可以写任何NSObject的子类。但是到了运行时就会原形毕露,所以这里会报错[mStr1Copy appendString:@"a"];

在看看我们自己实现的NSCopying吧,这正是我们所期待的的copy,两个对象地址不同,内容相同,你可以随便修改一个而不影响另一个。

        Person *person = Person.new;
        person.name = @"Noah";
        
        Person *person2 = person.copy;
        NSLog(@"\nperson address = %p name = %@\nperson2 address = %p name = %@", person, person.name, person2, person2.name);
        // person  address = 0x100203c40 name = Noah
        // person2 address = 0x100202820 name = Noah

NOTE:
因为NSCopying只是一个Protocol,所以没什么阻止你乱来,你可以乱七八糟的返回一些让其他人困惑的东西。所以在开发时,不要假定copy的行为!除了系统的那些是已知的外,第三方或其他人实现的记得要看源码。

上面说了那么多,现在终于入正题,作为修饰词的用法。
关于实例变量,setter和getter这里不多说,看这里http://www.jianshu.com/p/e442711a867a

假设有一个类有一个属性Person
@property (nonatomic, copy) Person *person;
当属性被copy修饰后,编译器生成的setter大概是这样的

- (void)setPerson:(Person *)person {
    _person = person.copy;
}

用strong的话是这样的

- (void)setPerson:(Person *)person {
    _person = person;
}

实际上作为修饰词,copy没有什么特别的。它只是在setter里加了个copy,只要属性实现了NSCopying。所以为什么在没有实现NSCopying的属性上使用时会崩溃了。

NOTE:
修饰immutable对象时必须要使用copy。这个面试经常问到,很多人也无法准确说出。
前面说过因为dynamic typing的关系,对象的类型是在运行时决定的。所以str在运行时会是NSMutableString类型。如果开发者本人并不知情并继续使用下去的话就会出现问题了。因为运行时的类型并不是期待的类型。

    NSString *str = nil;
    NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"str2"];
    str = mStr;

解决方法也很简单,对可变对象copy一下就好了。str = mStr.copy

关于mutableCopy

这个东西不能作为属性修饰词,但这里也有必要说说。
继承自NSObject的类都有copy和mutableCopy两个方法。当调用这两个方法后,系统会自动调用- (id)copyWithZone:(nullable NSZone *)zone- (id)mutableCopyWithZone:(nullable NSZone *)zone;
两个方法除了名字外都一样,所以看你怎么实现。

系统的immutable或mutable类都实现了NSCopying和NSMutableCopying,下面是一些特点。

  • 对可变对象进行copy,返回的会是一个不可变对象,并不是一般意义上的copy行为,因为copy后的对象类型不同了。
  • 对可变对象进行mutableCopy,返回的是另一个可变对象,这种才是copy行为,两个对象内容一样,类型一样,地址不同。
  • 对不可变对象进行copy,系统不会再创建对象,而是直接返回源对象地址。
  • 对不可变对象进行mutableCopy则返回一个可变对象,同样不是一般意义上的copy。
  • 在不可变对象上实现一般意义上的copy的方式是immutable.mutableCopy.copy

NOTE:
本文重点是,NSCopying是一个protocol,所以不要假定它实现了一个copy行为,系统实现的就不是我们期待的了。当然,在我们实现时,最好还是按一般的行为来实现,这样才不会让别人困惑。

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

推荐阅读更多精彩内容

  • 前言 不敢说覆盖OC中所有copy的知识点,但最起码是目前最全的最新的一篇关于 copy的技术文档了。后续发现有新...
    zyydeveloper阅读 3,252评论 4 35
  • 本文为转载: 作者:zyydeveloper 链接:http://www.jianshu.com/p/5f776a...
    Buddha_like阅读 824评论 0 2
  • 重要性与目标有关,凡有价值、有利于实现个人目标的就是要事。要事第一触及人生管理和时间管理的问题,如何分辨轻...
    一一小月阅读 763评论 2 0
  • 这几天没怎么看书,实在是灵感枯竭,写不出来东西,最近五城联创活动进行的比较紧迫,郭先生吃过晚饭又去加班了,要到夜里...
    刘怡洁大魔王阅读 448评论 0 0
  • 枫叶飘过 幸福依然记得水府的梦 问一问昨夜的泪 只是晚霞染红枫叶飘过的醉 梦想的天空总是下着雨 那或许就是枫叶飘过...
    米澜盛若阅读 149评论 1 3