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!

你可能感兴趣的:(iOS面试之定义NSString的属性为什么要用copy修饰?什么情况下使用strong? 什么情况下使用copy?)