最近和一个朋友一起做外包项目,发现他所有的NSString属性都是用strong来作为关键字的。但其实我们更推荐用copy来修饰一个immutable对象,用strong来修饰一个mutable对象,这也算是面试中很常见的问题吧。
首先,为什么NSString要使用copy,用strong会有什么样的问题?
我觉得回答这个问题最有力的一句话就是:因为父类指针可以指向子类对象。使用copy的话,可以不管传入的对象是mutable还是immutable的,都可以保证自己持有的是immutable的副本。口说无凭,让我们举个简单的🌰:
@property (nonatomic, copy) NSString *testStr;
NSMutableString *test = [[NSMutableString alloc] initWithString:@"123"];
self.testStr = test;
NSLog(@"self.testStr:%@",self.testStr);
[test appendString:@"tt"];
NSLog(@"self.testStr:%@",self.testStr);
打印结果:
MyTestProject[57848:1228549] self.testStr:123
MyTestProject[57848:1228549] self.testStr:123
可以看到,testStr并不受test字符串的变化而影响。下面我们稍微改一下,把testStr的声明改成strong,其他部分不变,打印结果:
MyTestProject[57881:1232402] self.testStr:123
MyTestProject[57881:1232402] self.testStr:123tt
事实上,这里的self.testStr和test都指向同一个地址,而没有新开辟一块空间,自然会跟随着变化了。而我们一般声明为NSString,是不希望它在我们不察觉的情况下被修改的,所以还是使用copy更为合理。
多说一句,如果在赋值的时候使用_testStr而不是self.testStr呢?答案是无论testStr声明为strong还是copy,都会随着test字符串变动而变动。而至于为什么,这就是另外一个问题了,属性和实例变量的区别。总之建议如果不是在init和dealloc中的时候,还是尽量使用self.来享受苹果带给我们的便利吧。
那么为什么immutable对象要使用strong呢?使用copy会有什么样的问题?
这个就涉及到深拷贝浅拷贝的问题了,网上有关这个的博客太多了,还是不赘述了。我还是只举个🌰吧:
@property (nonatomic, copy) NSMutableString *testStr;
NSString *test = @"123";
self.testStr = test;
[self.testStr appendString:@"tt"];
运行后我们会看到,程序直接crash掉了,log是:'Attempt to mutate immutable object with appendString:'。事实上,编译器在我们编码的时候也会给我们提示:incompatible pointer types assigning to "NSMutableString *" to "NSString *"。
之所以会crash,是因为我们在赋值的时候,copy自动帮我们做了一次深拷贝,返回了一个immutable的对象,也就是NSString对象,而NSString对象没有appendString方法,自然就crash掉了。