目录
一、IOS属性修饰符
二、属性修饰符分析
(一)、copy
(二)、assign
(三)、retain
(四)、strong
(五)、weak
(六)、readwrite
(七)、readonly
(八)、nonatomic/atomic
本文章为作者的学习总结文档,如有转载,侵权,请联系作者
主要的属性属性修饰符有以下几种:
如果按照MRC和ARC进行区分修饰符的使用情况,可以按如下方式进行分组:
1.MRC: assign/ retain/ copy/ readwrite、readonly/ nonatomic、atomic 等。
2.ARC: assign/ strong/ weak/ copy/ readwrite、readonly/ nonatomic、atomic 等。
属性修饰符对retainCount计数的影响:
1.alloc为对象分配内存,retainCount为1。
2.retain MRC下 retainCount +1。
3.copy一个对象变成新的对象,retainCount为1,原有的对象计数不变。
4.release对象的引用计数-1。
5.autorelease对象的引用计数retainCount -1,如果为0,等到最近一个pool结束时释放。
使用场景
1.一般情况下,copy可以用于对不可变变量的属性修饰中,主要是NSArray/NSDictionary/NSString,也可以用来修饰block。
2.在MRC和ARC下都可以使用。
应用举例
@property (nonatomic, copy, nullable) SDWebImageTransitionPreparesBlock prepares;
@property (nonatomic, copy) NSString *imageKey;
@property (nonatomic, copy) NSArray *frames;
问题:
a.block为什么要用copy修饰?
因为block在创建的时候,他的内存是分配在栈上的,而不是堆上。栈中的block的生命周期是和栈绑定的。他本身的作用域是属于创建时候的作用域,一旦在创建时候的作用域外面调用block将导致程序崩溃。为了能够在block的声明域外使用,所以只有使用copy后,block才会在堆中。并且block的retain行为默认是用copy的行为实现的。
b.为什么大部分要用nonatomic修饰?
为了使nonatomic修饰的属性访问速度更快。
c.nullable是什么属性修饰符
nullable表示修饰的属性或参数可以为空。
d.NSString为什么是用的是copy而不是strong?
当我们十分确定,要给属性NSString赋一个不可变的值是,用strong。如果用copy来修饰属性,在进行赋值的时候,会先做有个类型判断,如果赋的值是一个不可变的字符串,则走strong的策略,进行浅拷贝;如果是可变的字符串,就进行深拷贝创建一个新的对象。
如果我们确定是个属性赋值一个不可变的值,那么就用strong。但是很多情况下我们并不确定要赋的值是什么类型,所以我们一般还是用copy来修饰。这样保证了安全性。因为如果赋值的可变字符串,当他发生变化时,用strong修饰的属性的值也是会跟着变化的;而用copy修饰的属性,因为是深拷贝所有不会发生变化。
e.NSArray为什么是用的是copy而不是strong?
因为如果使用strong声明一个NSArray属性,那么这个属性有可能指向一个可变对象,并且可能该属性会在不知情的情况下被人修改。因此最好拷贝一份不可变的数据,确保不会意外的被修改。所以NSArray会使用copy修饰。
使用场景
1.在MRC和ARC下都可以使用。
2.用于非指针变量。一般用来修饰基础数据类型(NSInteger,CGFloat)和C数据类型(int,float,double)等。他的setter方法直接赋值,不进行任何retain操作。
应用举例
@property (nonatomic, assign) BOOL hasInitialBoundary;
@property (nonatomic, assign) unsigned long long bodyContentLength;
问题:
a.为什么assign一般修饰的都是基础数据类型?
因为使用assign修饰时,如果对象被销毁时并不会将指针置为nil。所以当对空间的对象销毁时指向对象的指针仍然存在,会造成野指针,当访问该对象时会crash报错EXC_BAD_ACCESS。而修饰基础数据类型,基础数据类型的空间开辟也是在栈空间,由系统进行自动销毁,所以不会造成野指针出现的情况。
b.为什么id要用assign修饰而不是retain?
因为如果使用retain可能会造成循环引用的问题,使用assign,不会使引用计数 +1,避免了出现循环引用的情况。
使用场景
1.一般情况下,retain用在MRC情况下,被retain修饰的对象,引用计数retainCount +1。
2.retain只能修饰OC对象,不能修饰非OC对象,比如说Core Foundation对象就是C语言框架,他没有引用计数,也不能用retain进行修饰。
3.retain一般用来修饰非NSString的NSObject类和其子类。
4.用于指针变量。就是说你定义了一个变量,然后这个变量在程序的运行过程中会被更改,并且影响到其他方法。一般是用于字符串( NSString,NSMutableString),数组(NSMutableArray,NSArray),字典对象,视图对象(UIView ),控制器对象(UIViewController)等。
应用举例
/// 右侧按钮
@property (nonatomic, retain) NSArray *rightBarItems;
/// 左侧按钮
@property (nonatomic, retain) NSArray *leftBarItems;
@property (nonatomic, readwrite, retain) UIView *inputAccessoryView;
问题:
a.MRC下assign和retain的区别?
assign只是简单的赋值操作,他引用的对象被释放,不会赋值为nil,有可能会造成野指针,可能会出现crash情况;retain会使对象的retainCount计数 +1,获得对象的拥有权,只有对象的引用计数为0的时候才会被释放,避免访问一个被释放的对象。
使用场景
1.strong表示对对象的强引用,对象的引用计数retainCount +1.
2.ARC下也可以用来修饰block,strong和weak两个修饰符默认是strong。
3.修饰控件可以用strong。
应用举例
@property (readwrite, nonatomic, strong) NSMutableArray *HTTPBodyParts;
@property (nonatomic, strong) UIImage *image;
问题:
a.为什么NSMutableArray用strong而不是copy? 例如:
@property (nonatomic, copy) NSMutableArray *mArray;//等同于
- (void)setMArray:(NSMutableArray *)mArray {
_mArray = mArray.copy;
}
mArray属性就成了NSArray,NSMutableArray只能用strong修饰,不存在有copy修饰的情况,写了就成了NSArray。如果是strong,直接是赋值_mArray = mArray;右边是什么,左边就是什么,并且是强引用新值。 所以用copy关键字的话,调用setter方法后。是对赋值对象进行深拷贝。并且拷贝的对象是copy的(不可变的)不是mutableCopy的(可变的)。所以用copy修饰的mutableArray也被视为Array了,所以再用mutableArray的方法就会发生崩溃。
使用场景
1.weak表示对对象的弱引用,被weak修饰的对象随时可被系统销毁和回收。
2.weak比较常用的地方就是delegate属性的设置。
3.用weak修饰弱引用,不会使传入对象的引用计数 +1。
应用举例:
@property (nonatomic, weak) id delegate;
问题:
a.修饰代理用weak还是assign?
assign是指针引用,不对引用计数操作,使用之后如果没有置为nil,就会变成野指针,所以如果用assign修饰。delegate指向的对象被销毁了,delegate中依然会保存之前对象的地址,delegate就变成了野指针。
而对于weak,delegate这个对象的销毁由外部控制。当delegate指向的对象销毁后,delegate = nil。
所以修饰delegate要用weak。
b.修饰代理为什么不用strong?
因为如果用strong修饰对象会对该对象进行强引用,外界不能销毁该对象,这样会导致循环引用。所以使用weak,weak不会引用计数 +1,这样可以避免循环引用。
使用场景
1.用readwrite修饰的时候表示该属性可读可写。
2.readwrite程序自动创建setter/getter方法。
3.系统默认的情况是readwrite。
应用举例
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
使用场景
1.用readonly修饰表示这个属性只可读,不可修改,一般常用在我们不允许外界改变只允许外界读取。
2.readonly程序创建getter方法。
应用举例
@property (readonly, nonatomic, assign) unsigned long long contentLength;
@property (readonly, nonatomic, assign, getter = isEmpty) BOOL empty;
使用场景
1.nonatomic非原子属性。他的特点是多线程并发性访问性能高,但是访问不安全。
atomic原子属性(atomic是通过锁定机制来确保其操作的原子性)。他的特点是安全但是是以耗费系统资源为代价,所以一般工程用nonatomic的时候比较多。
2.系统默认的是atomic,为setter方法加锁,而nonatomic不为setter方法加锁。
应用举例
@property (nonatomic, copy) NSString *boundary;
@property (nonatomic, strong) id body;
@property (nonatomic, assign) unsigned long long bodyContentLength;
问题:
a.多线程下加atomic就是安全吗?
这个说法是不正确的。atomic的安全只是在getter和setter方法的时候是原子操作,是安全的。但是其他方面是不在atomic管理范围之内的,这个时候不能保证安全。