属性修饰词

1、readOnly,readWrite

readOnly:

根据字面意思,你们都很容易知道是“只读”的意思,意味着只生成了getter方法,而没有生成setter方法,若是这时候调用setter方法,会报一个Assignment to readonly property错误css

PS:这里顺便说一下self.和_的区别html

self.就是调用property自动生成的getter和setter方法,而_则是直接去调用实例变量(property会自动生成一个实例变量,若是你重写了getter与setter方法,property自动生成的实例变量就无效了,须要手动去申明一个实例变量或者用@@synthesize).数组

回到正题,那么这意味着咱们就彻底没办法去修改readOnly的属性了吗?否则,若是你尝试一下setValue:forKey:,你就会发现居然改变成功了,amazing,让咱们来看看代码:安全

@interface MyFirstClass : NSObject @property (nonatomic, copy, readonly) NSString * string; @end #import "MyFirstClass.h" @implementation MyFirstClass - (instancetype) init{ self = [super init]; if (self) { _string = @"来改变我啊"; } return self; } @end - (void)viewDidLoad { [super viewDidLoad]; MyFirstClass * class = [MyFirstClass new]; NSLog(@"string === %@", class.string); [class setValue:@"改变了" forKey:NSStringFromSelector(@selector(string))]; NSLog(@"string === %@", class.string); } Log以下: 2018-03-16 11:08:58.932303+0800 PropertyDesc[5681:445705] string === 来改变我啊 2018-03-16 11:08:58.932454+0800 PropertyDesc[5681:445705] string === 改变了 

而若是这个时候在MyFirstClass里加入ruby

@implementation MyFirstClass

- (instancetype) init{
    self = [super init]; if (self) { _string = @"来改变我啊"; } return self; } + (BOOL) accessInstanceVariablesDirectly{ return NO; } @end 在运行,boom,系统会报如下错误 2018-03-16 11:19:21.619747+0800 PropertyDesc[6016:478446] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MyFirstClass 0x6040000088f0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key string.' 

没有找到当前要赋值的属性,那么accessInstanceVariablesDirectly到底是什么呢,咱们打开苹果的官方文档找到Key-Value Coding Programming Guide性能优化

 
963476FC-4E02-4196-BA64-302E16F5E8AD.png

在这里能够看到,若是这个方法设为YES,访问器就会去寻找名称为_<key>, _is<Key>, <key>, or is<Key>的成员变量,若是为NO,就会跳到第6步,第6步就会报[valueForUndefinedKey:]错误。
总结:
readOnly并不能彻底保证只读,咱们能够经过KVC尝试去修改其值。
PS:有兴趣的小伙伴能够尝试去修改别人的SDK,包括苹果爸爸的

 

readWrite:

这个实在没什么可说的,默认的修饰词就是readWrite,表明可读可写多线程

2、nonatomic、atomic

咱们先看一下官方文档app


 
苹果官网对二者的说法

atomic:

默认的属性修饰词,按官方文档上说即便从不一样的线程经过getter或setter方法去访问属性也能彻底的获取到或设置值,从这里也能够看出,atomic并非线程安全的,由于atomic只能保证经过setter和getter方法能获取到一个完整的value,若是A线程在getter,B、C线程在setter,可能A获取到的值是BC执行以后的值,也多是BC线程执行完以前的值,也多是B C线程任何一个线程执行完的值。
所以atomic的伪代码大概以下:ide

- (void)setString:(NSString *)string{ @synchronized(self) { if (_string != string) { [_string release];//MRC _string = [string copy]; } } } - (NSString *) string{ @synchronized(self) { return _ string; } } 

nonatomic:

相对而言,经过nonatomic修饰的属性,并无作锁的操做,多线程同时进行setter/getter操做,并不能保证获得一个完整的value,因此相对atomic来讲nonatomic修饰的属性访问速度更快,并且平时对线程安全咱们更倾向于使用信号量、NSLock和synchronized去控制线程安全,他们都能保证代码块的原子性,因此几乎全部的属性都用nonatomic去修饰。性能

3、assign、weak与strong

assign:

通常来讲,咱们都用assign去修饰OC的基本数据类型,but why?
由于assign并不会使对象的引用计数加1,也就是说若是用assign去修饰一个对象,这个对象会当即被释放,重要的是assgin在被释放的时候是不会自动置为nil,仍是保留对象的指针地址,会造成野指针,这个时候向其发送消息就会崩溃,简单实验的代码以下:

@interface MySecondClass : NSObject @property (nonatomic, assign) NSMutableArray * array; @end - (void) testMethodTwo{ MySecondClass * class = [[MySecondClass alloc] init]; self.secondClass = class; self.secondClass.array = [NSMutableArray array]; [self.secondClass.array addObject:@(0)]; } 

在运行到最后一步的时候程序会崩溃报EXC_BAD_ACCESS的错误,若是打断点的话会发如今执行到这步的时候array仍是有地址的。

weak:

若是把上面的代码
@property (nonatomic, assign) NSMutableArray * array;换成 @property (nonatomic, weak) NSMutableArray * array; 

这个时候程序并不会崩溃,若是你打个断点的话会发现array被自动置为nil,而OC的特性使得像nil发送消息并不会崩溃,这就是weak和assgin最大的区别,此外weak必须用于修饰对象,这和他自动置为nil相关,若是强行使用weak修饰基本数据类型,编译器会报一个大大的红色错误!

strong:

strong的做用和assign和weak偏偏相反,strong也是属性默认的修饰词,表明着被修饰的对象引用计数+1

若是把上面的代码
@property (nonatomic, assign) NSMutableArray * array;换成 @property (nonatomic, strong) NSMutableArray * array; self.secondClass.array = [NSMutableArray array]; 

最后一句代码能够解释为[NSMutableArray array]创造了一个对象A,此时A的引用计数为1,self.secondClass.array作为对象B,把A赋值给B的时候,A的引用计数加1,此时A的引用计数为2,B指向了A,而后编译器会自动对A进行释放操做(由于是局部变量),A的引用计数-1。在拥有B的对象不释放的时候,A的引用计数永远不可能为0,除非你手动释放或者把B指向一个新的对象,这样A永远不会被释放,这就是所谓的强引用。

4、copy与mutableCopy

 
苹果官网对对象拷贝的说法

在说copy与mutableCopy以前咱们先看看官方文档对深拷贝与浅拷贝的阐释,以下

深拷贝:

对象拷贝 - 从新申请一片内存保留这个对象,与原对象之间没有半点关系。

浅拷贝:

指针拷贝 - 实际上至关于引用计数+1,被拷贝的和拷贝的引用同一个对象。
接下来咱们分两个方面作测试:

1.对非集合类对象的copy操做,以NSString为例

对immutableObject作copy操做
NSString * string = [NSString stringWithFormat:@"1"]; NSString * copyString = [string copy]; NSString * mutableCopyString = [string mutableCopy]; NSLog(@"string:%p", string); NSLog(@"copyString:%p", copyString); NSLog(@"mutableCopyString:%p", mutableCopyString); 

Log以下:

2018-03-19 15:51:38.785253+0800 PropertyDesc[10283:759804] string:0xa000000000000311 2018-03-19 15:51:38.785435+0800 PropertyDesc[10283:759804] copyString:0xa000000000000311 2018-03-19 15:51:38.785518+0800 PropertyDesc[10283:759804] mutableCopyString:0x608000055150 

能够看出对string和copyString的地址是同样的,而mutableCopyString则不一样。

对mutableObject作copy操做
NSMutableString * string = [NSMutableString stringWithFormat:@"1"]; NSString * copyString = [string copy]; NSString * mutableCopyString = [string mutableCopy]; NSLog(@"string:%p - %@", string, string); NSLog(@"copyString:%p - %@", copyString, copyString); NSLog(@"mutableCopString:%p - %@", mutableCopyString, mutableCopyString); [string appendString:@",2"]; NSLog(@"copyString:%p - %@", copyString, copyString); NSLog(@"mutableCopString:%p - %@", mutableCopyString, mutableCopyString); 

Log以下:

2018-03-19 15:51:38.785670+0800 PropertyDesc[10283:759804] string:0x60400005a940 - 1 2018-03-19 15:51:38.785784+0800 PropertyDesc[10283:759804] copyString:0xa000000000000311 - 1 2018-03-19 15:51:38.785834+0800 PropertyDesc[10283:759804] copyString:0xa000000000000311 - 1 2018-03-19 15:51:38.785891+0800 PropertyDesc[10283:759804] mutableCopyString:0x60400005a910 - 1 2018-03-19 15:51:38.786037+0800 PropertyDesc[10283:759804] mutableCopyString:0x60400005a910 - 1 

能够看出对string与copyString、mutableCopyString三者的地址都是不一样的。
即便改变了原string的value,copyString与mutableCopystring也没有改变,这与下文对集合类对象得出的结论正好相反。

结论:

对 immutableObject进行 copy 操做是指针拷贝,mutableCopy 操做时对象拷贝。
对 mutable Object进行 copy 和 mutableCopy 都是对象拷贝。简单的表格图以下:

Object Handle Result
immutableObject copy 指针拷贝
immutableObject mutableCopy 深拷贝
mutableObject copy 深拷贝
mutableObject mutableCopy 深拷贝

2.对集合类对象的copy操做

对immutableObject作copy操做
NSArray * array = [NSArray arrayWithObject:@"1"]; NSArray * copyArry = [array copy]; NSMutableArray * mutableCopyArray = [array mutableCopy]; NSLog(@"array:%p", array); NSLog(@"copyArry:%p", copyArry); NSLog(@"mutableCopyArray:%p", mutableCopyArray); 

Log以下

2018-03-19 15:51:38.786167+0800 PropertyDesc[10283:759804] array:0x6000000094c0 2018-03-19 15:51:38.786278+0800 PropertyDesc[10283:759804] copyArray:0x6000000094c0 2018-03-19 15:51:38.786385+0800 PropertyDesc[10283:759804] mutableCopyArray:0x600000240030 

能够看出array与copyArray的地址是同样的,而mutableCopyArray则不一样。

对mutableObject作copy操做
NSMutableString * string = [NSMutableString stringWithFormat:@"1"]; NSMutableArray * array = [NSMutableArray arrayWithObject:string]; NSArray * copyArry = [array copy]; NSMutableArray * mutableCopyArray = [array mutableCopy]; NSLog(@"array:%p", array); NSLog(@"copyArry:%p", copyArry); NSLog(@"mutableCopyArray:%p", mutableCopyArray); [array addObject:@"2"]; [string appendString:@"1"]; NSLog(@"array:%p - %@", array, array); NSLog(@"copyArry:%p - %@", copyArry, copyArry); NSLog(@"mutableCopArray:%p - %@", mutableCopyArray, mutableCopyArray); 

Log以下

2018-03-26 13:36:38.786499+0800 PropertyDesc[10283:759804] array:0x600000240150 2018-03-26 13:36:38.786600+0800 PropertyDesc[10283:759804] copyArry:0x6000000095f0 2018-03-26 13:36:38.786698+0800 PropertyDesc[10283:759804] mutableCopyArray:0x6000002400f0 2018-03-26 13:36:38.786865+0800 PropertyDesc[10283:759804] array:0x600000240150 - ( 11, 2 ) 2018-03-26 13:36:38.787018+0800 PropertyDesc[10283:759804] copyArry:0x6000000095f0 - ( 11 ) 2018-03-26 13:36:38.787142+0800 PropertyDesc[10283:759804] mutableCopyArray:0x6000002400f0 - ( 11 ) 

What??不论是copy仍是mutableCopy咱们能够看到当咱们修改了string的值后,数组中的值都变了,可是在 [array addObject:@"2"];的时候两个复制出来的数组的对象并无变化?
这里咱们要提到一个新概念 不彻底深拷贝(也有人说是单层深拷贝 )------ 即虽然新开辟了内存地址,可是存放在内存上的值(也就是数组里的元素仍然指向原数组元素值,并无另外复制一份),因此说上文中的array和mutableCopArray相似于有一个或多个相交点的相交链表。
并且咱们也能够看到不论是copy仍是mutableCopy都是不彻底深拷贝,三者的地址都是不同的。

结论:

对immutableObject作copy是指针拷贝,作mutableCopy是不彻底深拷贝。
对mutableObject作copy或mutableCopy都是不彻底深拷贝。
有趣的是,这与上文的结论有相似之处。简单的表格图以下:

Object Handle Result
immutableObject copy 指针拷贝
immutableObject mutableCopy 不彻底深拷贝
mutableObject copy 不彻底深拷贝
mutableObject mutableCopy 不彻底深拷贝

而且若是打个断点能够发现对任何对象作copy操做返回的是一个不可变的对象,对任何对象作mutableCopy返回的是一个可变的对象(有兴趣的请自行验证)。

5、是用copy仍是strong?

经过上述对copy与strong的描述,copy和strong最终都会返回一个引用计数为1的对象,因此单单从内存管理上说copy和strong并没有任何区别,可是copy始终会返回一个不可变对象,他的值是不会被修改的。而strong不一样,被strong修饰的对象,可能会被可变对象赋值,从而在外部致使不可预料的被更改的状况。总而言之,是否使用copy或strong仍是根据具体场景来定,这里还有个性能优化的小技巧,若是copy的是可变的对象,会对它作一次彻底深拷贝/不彻底深拷贝,性能上是确定不如strong直接引用计数+1来的快。