文章都在简书上,需要的朋友请关注我简书
昨天立了flag,今天就来开始第一篇文章吧。
property的关键字是我们平常写代码随时都会用到的,只要声明一个属性,就会用到这些关键字,先从最简单的讲起吧。
一、如何使用
property的关键字分三类:
- 一类是表示原子性(也就是线程安全)的,有atomic和nonatomic,默认是atomic,acomic也就是线程安全,但是我们一般都用的nonatomic,因为atomic的线程安全开销太大,影响性能,即使需要保证线程安全,我们也可以通过自己的代码控制,而不用atomic。
- 一类是表示引用计数的,有assign(iOS5以前用unsafe_unretained),strong,weak,copy。
* assign:* assign用于非指针变量,一般用于基础类型和C数据类型,这些类型不是对象,统一由系统栈进行内存管理。
weak:对对象的弱引用,不增加对象的引用计数,也不持有对象,当对象消失后指针自动指向nil,所以这里也就防止了野指针的存在。
strong:对对象的强引用,会增加对象的引用计数,如果指向了一个空对象,会造成野指针,平常我们用得最多的应该也是strong了。
copy:建立一个引用计数为1的新对象,赋值时对传入值进行一份拷贝,所以使用copy关键字的时候,你将一个对象复制给该属性,该属性并不会持有那个对象,而是会创建一个新对象,并将那个对象的值拷贝给它。而使用copy关键字的对象必须要实现NSCopying协议。
unsafe_unretained:跟 weak 类似,声明一个弱引用,但是当引用计数为 0 时,变量不会自动设置为 nil,现在基本都用weak了。
- 一类是表示读写权限的,默认是readwrite(可读可写),还有就是readonly,当你希望暴露出来的属性不能被外界修改时就需要申明为readonly。
二、关键字与内存管理
这里重点讲一下与内存管理相关的这几个关键字。
直接上代码吧
@property (nonatomic,strong) NSString *strongStr;
@property (nonatomic,weak) NSString *weakStr;
测试
self.strongStr = @"string";
self.weakStr = self.strongStr;
self.strongStr = nil;
NSLog(@"strongStr=%@,weakStr=%@",self.strongStr,self.weakStr);
输出结果为 strongStr=(null),weakStr=string
有人可能会奇怪,为什么这里weakStr还是指向@”string”,strongStr都已经为为空了,weakStr也应该为空啊,weak关键字本来就不会使对象的引用计数加1。这里主要是因为NSString类型的赋值默认会加上copy,而copy会创建一个新的对象(后面再具体介绍)。也是就是说这里的赋值语句其实就是
self.strongStr = [@"string" copy];
self.weakStr = [self.strongStr copy];
由于NSString类型的特殊性,所以后面我自己创建一个Person类继续进行试验。
同样的还是weak和strong
@property (nonatomic,strong) Person *strongPerson;
@property (nonatomic,weak) Person *weakPerson;
试验代码一样
self.strongPerson = [Person new];
self.weakPerson = self.strongPerson;
self.strongPerson = nil; NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
输出结果为: strongStr=(null),weakStr=(null)
这里就足以说明weak修饰的属性并不会使引用计数增加
稍微修改代码,把strongPerson设置为nil
self.strongPerson = [Person new];
self.weakPerson = self.strongPerson;
self.weakPerson = nil;
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
输出结果如下: strongStr=<Person: 0x600000007d50>,weakStr=(null)
说明weak修饰的属性只是对对象的弱引用,并不会真正的持有该对象。
再次修改代码
Person *p = [Person new];
self.strongPerson = p;
self.weakPerson = self.strongPerson;
p = nil;
NSLog(@"strongStr=%@,weakStr=%@",self.strongPerson,self.weakPerson);
输出结果为: strongStr=<Person: 0x600000200b50>,weakStr=<Person: 0x600000200b50>
这里不用多说,因为strong属性会强引用该对象并使该对象的引用计数+1,所以即使把p设置为nil,该对象也并没有释放,要想释放该对象,还得把strongStr设置为nil。 self.strongPerson = nil;
这样输出结果就是 strongStr=(null),weakStr=(null)
了。
再来看看copy关键字
为了方便试验,给person加了一个name属性。 @property (nonatomic,copy) NSString *name;
NSString *a = @"xiaoming";
Person *p = [Person new];
p.name = a;
NSLog(@"p.name=%@",p.name);
毫无疑问,输出结果为xiaoming。但是此时改一改a的值呢?
NSString *a = @"xiaoming";
Person *p = [Person new];
p.name = a;
a = @"xiaohua";
NSLog(@"p.name=%@",p.name);
输出结果:p.name=xiaoming
。如前所说,copy关键字修饰的属性是将对象拷贝一份赋值,所以你改变原对象并不会对拷贝后的对象有任何改变。
在这里要重点说一下,使用NSMutableArray,NSMutableDictionary等可变集合对象的时候千万不要用copy,这里用copy 99%会出错,因为当你给该属性赋值时它会自动调用对象的copy方法,从而将可变集合转换成不可变集合,把一个不可变集合赋值给一个可变集合,就会造成错误。
感觉写得有些啰嗦,我觉得主要是给新人看吧,毕竟我自己当初一直对这些关键字也是懵懵懂懂的,如果看了我这篇文章还是不能理解,那应该是完全不了解内存管理方面的知识,MRC、ARC应该去了解一下,如果后面有需要,我可能会写一篇相关的文章。
另外,还请大神门多多指教。。。。