iOS面试之定义NSString的属性为什么要用copy修饰?什么情况下使用strong? 什么情况下使用copy?

在面试iOS程序员的时候,大家经常被问到的一个问题就是,在定义一个NSString类型的属性时,为什么要用copy修饰?通常得到的回答都是,

“为了防止修改这个属性时,会同时修改了原对象的值。”

我不知道这个结论他们是怎么得出来的,我只想说,错,错极了。

下面我就给大家解释下为什么那么回答是错的,直接上代码。(文章最后有如何正确地回答这个问题,着急的同学可以直接翻到最后)

定义4个属性

nameStrong为不用copy修饰的情况,nameCopy为用copy修饰的情况。normalName和mutableName为两种原字符串。

@property (nonatomic, strong)   NSString         *nameStrong;    // 用strong修饰
@property (nonatomic, copy)     NSString         *nameCopy;      // 用copy修饰
@property (nonatomic, copy)     NSString         *normalName;    // 原字符串-不可变
@property (nonatomic, strong)   NSMutableString  *mutableName;   // 原字符串-可变

原字符串为不可变的情况

先说原字符串为不可变的情况,这种情况比较简单。
给normalName赋值,并把normalName赋值给nameStrong和nameCopy,如下

    self.normalName     = @"1111";
    self.nameStrong     = self.normalName;
    self.nameCopy       = self.normalName;
    
    NSLog(@"\nnormalName: %@ - normalName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.normalName, _normalName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);

打印结果为:

normalName: 1111 - normalName地址: 0x10c1a3220
nameStrong: 1111 - nameStrong地址: 0x10c1a3220
nameCopy: 1111 - nameCopy地址: 0x10c1a3220

你会发现,nameStrong和nameCopy同原字符串的地址是一样的,所以值肯定也是一样的。

如果对原字符串normalName进行改变呢?严谨来说,normalName为不可变类型,只能重新进行赋值,如下:

    self.normalName    = @"1111";
    self.nameStrong    = self.normalName;
    self.nameCopy      = self.normalName;
    
    NSLog(@"\nnormalName: %@ - normalName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.normalName, _normalName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);
    
    self.normalName = @"2222";
    
    NSLog(@"\nnormalName: %@ - normalName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.normalName, _normalName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);

打印结果为:

normalName: 1111 - normalName地址: 0x101a07220
nameStrong: 1111 - nameStrong地址: 0x101a07220
nameCopy: 1111 - nameCopy地址: 0x101a07220

normalName: 2222 - normalName地址: 0x101a07260
nameStrong: 1111 - nameStrong地址: 0x101a07220
nameCopy: 1111 - nameCopy地址: 0x101a07220

你会发现,nameStrong和nameCopy的地址并没有发生变化,还是同最初normalName的地址是一样的,所以值没变,但normalName重新赋值后,地址发生了变化,指针指向了一块新的地址。

结论:如果原字符串为不可变类型字符串,使用copy或strong修饰NSString效果是一样的。

原字符串为可变的情况

之所以面试中会有这个问题,主要就是考虑到有这种情况。
给mutableName进行赋值,并把mutableName赋值给nameStrong和nameCopy,如下:

    self.mutableName    = [NSMutableString stringWithString:@"1111"];
    self.nameStrong     = self.mutableName;
    self.nameCopy       = self.mutableName;
    
    NSLog(@"\nmutableName: %@ - mutableName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.mutableName, _mutableName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);

打印结果为:

mutableName: 1111 - mutableName地址: 0x604000258900
nameStrong: 1111 - nameStrong地址: 0x604000258900
nameCopy: 1111 - nameCopy地址: 0xa000000313131314

你会发现,三个属性的值是一样的,但nameStrong同mutableName指向的是同一块地址,而nameCopy则是指向的一块新的地址。那是因为在把mutableName赋值给nameCopy时,自动进行了深拷贝,把mutableName的内容复制了一份,并新开了一块内存来存储,然后让nameCopy指向了这个新的地址。

如果对mutableName进行改变呢?如下:

    self.mutableName     = [NSMutableString stringWithString:@"1111"];
    self.nameStrong      = self.mutableName;
    self.nameCopy        = self.mutableName;
    
    NSLog(@"\nmutableName: %@ - mutableName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.mutableName, _mutableName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);
    
    [self.mutableName appendString:@"aaaa"];

    NSLog(@"\nmutableName: %@ - mutableName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.mutableName, _mutableName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);

打印结果为:

mutableName: 1111 - mutableName地址: 0x6000004453a0
nameStrong: 1111 - nameStrong地址: 0x6000004453a0
nameCopy: 1111 - nameCopy地址: 0xa000000313131314

mutableName: 1111aaaa - mutableName地址: 0x6000004453a0
nameStrong: 1111aaaa - nameStrong地址: 0x6000004453a0
nameCopy: 1111 - nameCopy地址: 0xa000000313131314

你会发现,nameStrong的值也被改变了,但nameCopy并没改变。这就是为什么要使用copy的原因了。

如果是直接对mutableName进行赋值操作,则同normalName一样,对strongName和copyName都不会有影响,只是改变的它自己,如下:

    self.mutableName     = [NSMutableString stringWithString:@"1111"];
    self.nameStrong      = self.mutableName;
    self.nameCopy        = self.mutableName;
    
    NSLog(@"\nmutableName: %@ - mutableName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.mutableName, _mutableName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);
    
//    [self.mutableName appendString:@"aaaa"];
    self.mutableName = [NSMutableString stringWithString:@"2222"];

    NSLog(@"\nmutableName: %@ - mutableName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.mutableName, _mutableName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);

打印结果为:

mutableName: 1111 - mutableName地址: 0x60400024e970
nameStrong: 1111 - nameStrong地址: 0x60400024e970
nameCopy: 1111 - nameCopy地址: 0xa000000313131314

mutableName: 2222 - mutableName地址: 0x60400024edf0
nameStrong: 1111 - nameStrong地址: 0x60400024e970
nameCopy: 1111 - nameCopy地址: 0xa000000313131314

同normalName情况一样,不再解释。

现在又有一个新的问题,如果为了防止自己被改变,必须要用copy修饰吗?其实不是,你也可以用strong修饰它,但赋值的时候记得调用一下copy方法,比如我给nameStrong赋值时,调用copy方法,如下:

    self.mutableName    = [NSMutableString stringWithString:@"1111"];
    self.nameStrong     = [self.mutableName copy];
    self.nameCopy       = self.mutableName;
    
    NSLog(@"\nmutableName: %@ - mutableName地址: %p\nnameStrong: %@ - nameStrong地址: %p\nnameCopy: %@ - nameCopy地址: %p",
          self.mutableName, _mutableName, self.nameStrong, _nameStrong, self.nameCopy, _nameCopy);

打印结果为:

mutableName: 1111 - mutableName地址: 0x60000044ef40
nameStrong: 1111 - nameStrong地址: 0xa000000313131314
nameCopy: 1111 - nameCopy地址: 0xa000000313131314

你会发现,nameStrong的地址同mutableName的地址也不一样了,nameStrong和nameCopy指向了同一块新的地址。
虽然使用这种方式可以解决使用strong修饰的问题,但一般情况下定义属性时直接使用copy修饰更方便。

结论

那应该怎么回答这个问题呢?我认为可以这样回答:
为了防止在把一个可变字符串在未使用copy方法时赋值给这个字符串对象时,修改原字符串时,本字符串也会被动进行修改的情况发生。

Have fun!

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

推荐阅读更多精彩内容