天天看点

(0087)iOS开发之NSString属性为什么要用copy来修饰?

这个问题既是一个面试题,也是开发中经常遇到的问题,NSString 属性到底用copy 还是 strong ?其实如果明白的两者的区别也就不会疑惑了,其实都可以,只是如果你不明白两者的实质的区别,有可能会出现难以发现的异常。但是我们遇到的又很少所有经常用哪个都行,但是不知道有何区别,在此我实际验证一下。记录这个经常模糊不清的问题。

1.快速搭建一个demo

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    NSLog(@"\n");

    // 验证不可变对象NSString
    [self test];
    
    // 验证可变对象NSMutableString
    // [self test22];

    // 如果用NSString赋值的话strong和copy(此刻是浅拷贝)是没有区别的
    
    // 如果用NSMutableString赋值的话strong没有只是增加了str1的计数器,并没有开辟新的内存
    // copy的话开辟了新的内存,对string的内容进行修改的话,strong的字符串内容改变了,而copy的并没有改变
    // 如果需要改变的话可以用strong,如果不需要改变的话用copy
    
    // 所以属性指向可变对象时应注意;
}
           
- (void)test{
    NSString *string = [NSString stringWithFormat:@"测试文字"];//注释1
    
    self.strongStr = string;
    self.copyssStr = string;
    NSLog(@"String:%@   strongStr:%@   copyssStr:%@",string,self.strongStr,self.copyssStr);
    NSLog(@"String   : %p, %p", string, &string);
    NSLog(@"Strong属性: %p, %p",_strongStr, &_strongStr);
    NSLog(@"Copy  属性: %p, %p",_copyssStr, &_copyssStr);
    NSLog(@"\n");

    string = @"我变了,你没变";
    NSLog(@"我变 String:%@   strongStr:%@   copyssStr:%@",string,self.strongStr,self.copyssStr);
    NSLog(@"我变 String   : %p, %p", string, &string);
    NSLog(@"我变 Strong属性: %p, %p",_strongStr, &_strongStr);
    NSLog(@"我变 Copy  属性: %p, %p",_copyssStr, &_copyssStr);
    // NSString总结:用NSString赋值的话strong和copy(此刻是浅拷贝)是没有区别的;从新赋值,相当于指向了新的一个对象,string指向变了,而strong和copy指针(此刻是浅拷贝
    // 还是指向的原来的对象,所以说不变。
    // 所以不可变对象的copy 和strong 与可变对象的copy 一样效果,赋值后copy 和 strong 指针指向的值都不变;

    /*
     1. 当原字符串是NSMutableString时,Strong属性只是增加了原字符串的引用计数,而Copy属性则是对原字符串做了次深拷贝,产生一个新的对象,且Copy属性对象指向这个新的对象,且这个Copy属性对象的类型始终是NSString,而不是NSMutableString,因此其是不可变的。
     2. 这里还有一个性能问题,即在原字符串是NSMutableString,Strong是单纯的增加对象的引用计数,而Copy操作是执行了一次深拷贝,所以性能上会有所差异(虽然不大)。如果原字符串是NSString时,则没有这个问题。
     所以,在声明NSString属性时,到底是选择strong还是copy,可以根据实际情况来定。不过,一般我们将对象声明为NSString时,都不希望它改变,所以大多数情况下,我们建议用copy,以免因可变字符串的修改导致的一些非预期问题。
     */
}
           
- (void)test22{
    
    //
    NSMutableString *string = [NSMutableString stringWithFormat:@"测试文字"];
    
    self.strongStr = string;
    self.copyssStr = string;
    NSLog(@"String:%@   strongStr:%@   copyssStr:%@",string,self.strongStr,self.copyssStr);
    NSLog(@"String 地址: %p, %p", string, &string);
    NSLog(@"Strong 地址: %p, %p",_strongStr, &_strongStr);
    NSLog(@"Copy   地址: %p, %p",_copyssStr, &_copyssStr);
    NSLog(@"\n");

    
    [string appendString:@"我变了,你没变"];
    NSLog(@"append 后 String:%@   strongStr:%@   copyssStr:%@",string,self.strongStr,self.copyssStr);
    NSLog(@"append 后 String 地址: %p, %p", string, &string);
    NSLog(@"append 后 Strong 地址: %p, %p",_strongStr, &_strongStr);
    NSLog(@"append 后 Copy   地址: %p, %p",_copyssStr, &_copyssStr);
    NSLog(@"\n");

    // NSMutableString总结:
    // 如果string 的指针不变,内存地址不变,值变化,则strongStr 的值跟着变化,指针不变,地址不变;而copyssStr 则不会任何变化;
    
    
    string = [NSMutableString stringWithFormat:@"ccvcc"];
    NSLog(@"Mutable 后 String:%@   strongStr:%@   copyssStr:%@",string,self.strongStr,self.copyssStr);
    NSLog(@"Mutable 后 String 地址: %p, %p", string, &string);
    NSLog(@"Mutable 后 Strong 地址: %p, %p",_strongStr, &_strongStr);
    NSLog(@"Mutable 后 Copy   地址: %p, %p",_copyssStr, &_copyssStr);
    // 总结:Mutable 相当于重新alloc一个对象, string 指针的指向变了,指向了新的对象的地址,strongStr 指针不变,指向的还是原来对象的地址,地址不变;而copyssStr已经去string没有关系,则不会任何变化;
    

    // 这里我想验证下strongStr 指向的是原来的还是新的对象的
    [self performSelector:@selector(shows) withObject:nil afterDelay:10];
}
- (void)shows {
    NSLog(@"shows 后 strongStr:%@   copyssStr:%@",self.strongStr,self.copyssStr);
}
           

上面代码中有自己对比得出的结论,推出多用copy 是因为我们使用属性一般是全局的,不期望 *string被改变时,也改变了copy 属性的值。我们希望的是self.copyssStr = @"";来改变

参考:https://www.jianshu.com/p/8bbe01e53114

:https://blog.csdn.net/itianyi/article/details/9018567

:https://blog.csdn.net/summer_csdn123/article/details/52190879

:https://www.jianshu.com/p/b3873ac9259b

继续阅读