做过iOS开发的同学肯定都用过copy属性,但是copy属性和strong的区别可能还有很多人并不是很清楚,这一篇文章就专门讲一讲copy这个属性。
首先抛出本篇文章的结论:
可以简单的把copy属性理解为把目标参数copy,也就是拷贝之后再赋值给成员变量。
以下是生成set方法的源码,可以参考。
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
从这里可以看出来copy和strong的区别主要在于copy这个动作。
那么copy的作用到底是什么?这个动作对于不同Object又有什么不同作用呢?copy和mutableCopy又有什么区别呢?
这里再一次抛出结论:
对不可变变量(例如NSString)进行不可变拷贝(copy),是浅拷贝(引用计数+1),其他情况都是深拷贝。
对不可变变量(例如NSString)来说定义成copy,更加安全,但是效率也更低
为什么更加安全?
我们把一个变量定义成NSString,那一定是我们不希望它的值被任意改变。如果定义属性的时候使用strong,那就有可能存在被任意,或者意料之外改动的情况。
我们来看一个例子:
@interface ViewController ()
@property (nonatomic, strong) NSString* strongString;
@property (nonatomic, copy) NSString* copyedString;
@end
- (void)copyTest
{
NSMutableString* mstr = [NSMutableString stringWithFormat:@"I'am mutableString"];
self.strongString = mstr;
self.copyedString = mstr;
[self printAdress:mstr];
[mstr insertString:@"and I'am copyed" atIndex:mstr.length];
[self printString:mstr];
}
- (void)printAdress:(NSString*)str
{
NSLog(@"*****************");
NSLog(@"str:%p",str);
NSLog(@"storng:%p",(_strongString));
NSLog(@"copy:%p",(_copyedString));
}
- (void)printString:(NSString*)str
{
NSLog(@"+++++++++++++");
NSLog(@"str:%@",str);
NSLog(@"storng:%@",(_strongString));
NSLog(@"copy:%@",(_copyedString));
}
输出结果:
*****************
str:0x60000058fe10
storng:0x60000058fe10
copy:0x60000058fae0
+++++++++++++
str:I'am mutableStringand I'am copyed
storng:I'am mutableStringand I'am copyed
copy:I'am mutableString
由于strongString是定义成了strong,在被赋予一个NSMutableString的变量mstr之后,对mstr进行修改,strongString也被修改了。这相当于对一个不可变变量间接进行了修改。
为什么更低效?
原因很简单,因为需要深拷贝,需要重新开辟空间并且赋值。
但是,不是把属性定义成copy就万事大吉了,需要注意的是在类内部还是需要使用self.copyedString的方式或者[self setCopyedString]调用,如果直接_copyedString=mstring的方式还是不会起到效果,因为没有调用setter方法
Block属性最好使用copy
虽然对于Block来说,ARC环境下使用copy和strong的效果一样(因为即使是定义成strong,赋值的时候依然会为我们拷贝),但是我们最好使用copy修饰。
原因跟Block的内存分布有关系。
Block根据他们在内存的位置分为三种:
1.NSStackBlock(在栈区,超出作用域就会自动释放)。
2.NSMallocBlock(在堆区,生命周期由程序员手动管理)。
3.NSGlobalBlock(全局区)。
对于栈区的Block可以进行一次拷贝,把栈区的Block拷贝到堆区。
在MRC下必须使用copy,以完成拷贝。沿用到ARC下最好也用copy,以表明其工作原理。
关于Block的内存分布是一个非常复杂的话题,这里就不展开了。