通常我们在开发iOS都会使用@property来定义属性,那么我们今天就来仔细探究一下@property。
@property的作用
@property声明的属性会自动生成一个带下划线的成员变量,并自动生成对应属性的setter/getter方法,但你在代码不会看到它们,但能够使用。
比如使用self.属性的时候就会使用就会使用到setter/getter方法,setter/getter方法中默认使用带下划线的成员变量。
结论:@property = _成员变量 + setter/getter方法
@property的属性关键字
关键字分为四类:原子性、内存管理、读写权限和大家容易漏掉的为空性。
原子性:
- atomic(默认):这个属性是为了保证程序在多线程情况下的线程安全,编译器会自动生成互斥加锁代码,避免该变量的读写不同步问题。
- nonatomic:如果该对象无需考虑多线程的线程安全,通常使用这个关键字,这样会让编译器少生成互斥加锁代码,可以提高性能。
如果同时有两个或者两个以上的线程同时对一个属性就行赋值,这个时候为了保证属性调用前后的一致性,我们就要做些保护操作,这就是传说中的线程安全。
线程安全就是为了保证在多线程环境中,有且仅有一个线程能对当前属性进行set和get操作,其他线程必须等待其完成操作之后再进行操作。
我们通常会尽量人为避免多线程安全问题,所以一般使用nonatomic来提高性能。
内存管理:
- assign(默认):setter方法直接赋值,不进行任何retain操作,不改变引用计数。一般用来处理基本数据类型(NSInteger、CGFloat、int、char等)。
- strong:强引用,也是通常说的引用,持有对象会使被持有对象的引用计数+1,持有对象的存亡直接决定了被持有对象的存亡。如果一个对象引用计数为0,即不被任何对象持有,并且此对象不再显示在列表中,则此对象就会从内存中释放。当所指对象被销毁时,指针会自动被置为
nil
,防止野指针。 - weak:弱引用,持有对象不会使被持有对象的引用计数增加,也不决定对象的存亡。即使一个对象被持有无数个弱引用,只要没有强引用指向它,那么还是会被销毁。当所指对象被销毁时,指针会自动被置为
nil
,防止野指针。 - copy:建立一个索引计数为1的对象,然后释放旧对象 。用来复制对象,对不可变对象使用copy相当于retain,只是引用计数+1。适用于 NSString、NSArray、NSDictionary等不可变对象,以及block。切记可变对象不要使用。
- retain:释放旧的对象(release),将旧对象的值赋给新对象,再令新对象引用计数为1,但现在已经不常用了。适用于多数oc对象。
读写权限:
- readwrite(默认):其实就是字面意思“读写”,让我们能使用setter/getter方法进行读写操作。
- readonly:只读,只能使用getter方法,不会生成setter方法,但是可以通过KVC访问成员变量来赋值。
这两个属性的真正价值,不是提供成员变量访问接口,而是控制成员变量的访问权限。
为空性:
- nullable(默认):表示这个属性可以为空,只能修饰对象。
- nonnull:表示这个属性不能为空,只能修饰对象。
书写规范:
@property(nullable, nonatomic, readonly, strong) NSString *currentTitle;
这是UIButton中的一个公共属性,苹果官方的写法顺序是:1、为空性 2、原子性 3、读写权限 4、内存管理。
setter/getter方法
在自定义控件的时候我们一般会选择重写属性的setter/getter方法,在其中完成一些操作。
所以我们就需要来了解一下setter/getter方法机制。
什么时候调用setter/getter方法?
使用 self.属性(或其他变形访问方式)进行访问的时候就会调用setter/getter方法。
而只有当self.属性出现在 = 左边的时候调用setter方法,其余情况会调用getter方法。
在setter/getter方法中一般使用_成员变量来进行赋值或取值,因为_成员变量对属性只是使用,不会调用setter/getter方法。
如果在setter/getter方法内部使用self.属性就会再调用setter/getter方法,就会造成循环调用setter/getter方法导致程序崩溃。
举个栗子:setBookModel -> self.bookModel -> setBookModel 形成循环调用,造成死循环。
-(void) setBookModel:(MYBookModel *)bookModel
{
self.bookModel = bookModel;
}
正确用法:
-(void) setBookModel:(MYBookModel *)bookModel
{
_bookModel = bookModel;
}
@dynamic与@synthesize
@property还有两个对应的词:@dynamic与@synthesize。
@synthesize:如果没有手动实现setter方法和getter方法,那么编译器会自动为生成setter方法和getter方法。
@syntheszie var = _var;//将属性和成员变量进行绑定
在ARC下,通过@property声明的属性,编译器就自动使用@synthesize,并且不会显示出来,所以现在我们都看不到。
当然我们也可以显示地使用@synthesize来设置对应的成员变量,而不用编译器自动生成的,但一般不用。
只有在同时重写setter和getter方法时会使_成员变量失效,只重写setter或getter方法都没有问题。
因为当重写了setter和getter方法之后,@property默认生成的@synthesize就会失效,这也就意味着类不会自动生成出来_成员变量了。
如果重写的setter和getter方法中需要使用_成员变量,此时我们就需要显示地使用@synthesize了。
@dynamic:告诉编译器,该属性的setter与getter方法由我们自己实现,不自动生成。
@dynamic var;
但是一个属性被声明为@dynamic,你却没有提供setter以及getter方法,编译时不会有问题,运行时就会报错。