从Objective-C copy strong 说开去

为什么NS* 要使用copy修饰?
为什么NSMutable*要用strong修饰?

First, 我们得看下copy和strong有什么区别,它们都是属性的修饰符,关键在于它们对应了不同的setter方法实现

strong :
-(T)setA:(T)a {
    if (_a != nil) {
        [_a release];
    }
    [a retain];
    _a = a;
    return _a;
}

copy:
-(T)setA:(T)a {
    if (_a != nil) {
        [_a release];
    }
    _a = [a copy]; // 如果a是可变类型,则返回一个不可变副本,
                   // 如果a是不可变类型,则返回其本身
    return _a;
}
这里插播一段小知识:下划线使用属性和self.使用属性有啥区别

使用下划线对属性赋值时,是直接对属性赋值,如

@property (nonatomic, strong) T *a;
T *b = [[T alloc] init];
_a = b; 

这里 b 的引用计数不会加一,_a 直接指向了b指向的地址,引用计数仍为一,如果使用MRC的情况下,

[b release]; 

这时再进行

[_a release]; 

则会崩溃,原因就在于引用计数原先没有增加,

[b release];

后引用计数已经为0了。当引用计数为0时,系统就会调用delloc释放该对象和该对象所拥有的实例,该对象就销毁了。
而使用self.对属性赋值时则不同,它实际是调用了属性的setter方法

欢迎回来,strong修饰的属性赋值时指向的是同一块地址,引用计数加一,属于浅拷贝,而使用copy修饰的属性在赋值时将获得右值的一份不可变的副本

For example,如果使用strong修饰一个NSString *属性aStr

@property (nonatomic, strong) NSString *aStr;
NSMutableString *mStr = [[NSMutableString alloc] init];
self.aStr = mStr;     //这里mStr引用计数为2
_aStr = mStr;         //这里mStr引用计数仍为2

使用NSString *是说明定义的属性在初始化后其内容不应该再改变了,NSMutableString *是说明定义的属性内容可以改变,而这时如果将mStr赋值给aStr,因为使用的是strong修饰符,那么aStr将指向mStr的地址,mStr改变值的时候aStr的值也改变了,将与原意相悖;而使用copy修饰就不会有这情况产生了。

而如果将一个可变属性用copy修饰,则可能会造成灾难性结果(请先再回看下上文中copy对应的setter方法):

@property (nonamatic, copy) NSMutableString *mStr;
_mStr = [[NSMutableString alloc] init];

这里是直接赋值,mStr还是得到一个可变类型的结果,但是

self.mStr = [[NSMutableString alloc] init];

如果使用了self. 那么将会调用setter方法,则mStr将会得到一个不可变类型的结果 __NSCFConstantString,按照原意mStr是可以改变的,而这里mStr的类型已经变为不可变类型了,此时再对其进行append等操作,则会crash

这就是为什么NS* 使用copy修饰,而NSMutable*则使用strong修饰的原因了

那么这里为什么类型会变呢,明明声明定义的是NSMutableString类型的属性,为什么结果得到的却不是呢,这里就需要了解另外一个概念--类簇

                                                     -- Thusday, 13 July 2017

你可能感兴趣的:(从Objective-C copy strong 说开去)