一、readOnly,readWrite
readOnly:
只读,只有getter方法没有setter方法。这时候调用setter方法的话,会Assignment to readonly property错误。
当然如果是系统的类或者第三方的类还是想要修改该属性的话。可以使用setValue:forKey:来修改属性,因为setValue:forKey:寻找的路径不太一样。
如果自己写的类不想让外界修改自己的属性时。可以加上这句话。
+ (BOOL) accessInstanceVariablesDirectly{
return NO;
}
官方文档是这样写的

在这里可以看到,如果这个方法设为YES,访问器就会去寻找名称为_<key>, _is<Key>, <key>, or is<Key>的成员变量,如果为NO,就会跳到第6步,第6步就会报[valueForUndefinedKey:]错误。
总结:
readOnly并不能完全保证只读,我们可以通过KVC尝试去修改其值。
PS:有兴趣的小伙伴可以尝试去修改别人的SDK,包括苹果爸爸的
readWrite:
这个实在没什么可说的,默认的修饰词就是readWrite,代表可读可写
二、nonatomic、atomic(非原子性,原子性)
这个经常跟多线程的线程安全扯在一起,这里可以非常明确的告诉你,以上两种模式都不能保证线程的安全性。他们两个的区别在于原子性,保证了返回的对象是完整的,而非原子性不能保证对象是完整的(这里是为了提升工作速度)。
但是如果我们想做到线程安全。可以采用信号量、NSLock和synchronized去控制线程。并且他们可以保证原子性。
我们平常开发中对象默认为nonatomic。
三、assign、weak与strong
assign:
一般来说,我们都用assign去修饰OC的基本数据类型
因为assign并不会使对象的引用计数加1,也就是说如果用assign去修饰一个对象,这个对象会立即被释放,重要的是assgin在被释放的时候是不会自动置为nil,还是保留对象的指针地址,会形成野指针,这个时候向其发送消息就会崩溃,简单实验的代码如下:
#import "ViewController.h"
@interface MyClass : NSObject
@property (nonatomic, assign) NSMutableArray * array;
@end
@implementation MyClass
@end
@interface ViewController : UIViewController
@property (nonatomic, strong) MyClass * mClass;
@end
@implementation ViewController
- (void) testMethodTwo{
self.mClass = [[MyClass alloc] init];
self.mClass.array = [NSMutableArray array];
[self.mClass.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.mClass.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指向一个新的对象,这就是所谓的强引用。
四、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返回的是一个可变的对象(有兴趣的请自行验证)。
五、是用copy还是strong?
通过上述对copy与strong的描述,copy和strong最终都会返回一个引用计数为1的对象,所以单单从内存管理上说copy和strong并无任何区别,但是copy始终会返回一个不可变对象,他的值是不会被修改的。
而strong不同,被strong修饰的对象,可能会被可变对象赋值,从而在外部导致不可预料的被更改的情况。
总而言之,是否使用copy或strong还是根据具体场景来定,这里还有个性能优化的小技巧,如果copy的是可变的对象,会对它做一次完全深拷贝/不完全深拷贝,性能上是肯定不如strong直接引用计数+1来的快。
网友评论