盒子
导航
文章目录
  1. 一、基础语法
  2. 二、Attributes
    1. 1、用于线程的修饰符
    2. 2、用于内存管理的修饰符
    3. 3、用于控制访问的修饰符
    4. 4、用于取别名的修饰符
  3. 参考资料

Objective-C基础:Property

Objective-C的属性(Property)是Objective-C的类封装和保存数据的方式,只需要使用@property来声明一个属性,编译器会自动合成(synthesize)实例变量(默认以下划线_加上属性名字来命名)和访问器方法(accessor method,即getter/setter方法),属性还提供了一组修饰符来描述与线程、内存管理相关的行为。

一、基础语法

基本语法如下:

@property() ;

例如:

@property (strong, nonatomic) NSString str;
@property (assign, nonatomic, readonly, getter=isComplete) BOOL complete;
@property (weak, nonatomic) IBOutlet UIView
testView;

对于.h.m,一般将开放的接口定义于.h的 @interface 中,将私有的属性和方法以类扩展的方式在.m中定义。即在.h中定义的属性方法是可被外部访问的,而定义于.m中的属性和方法则是私有的。

在旧版本的XCode中声明了属性之后需要在.m文件的实现中使用@synthesize <PropertyName>来让编译器合成实例变量和访问器方法,但是在新版的XCode中就不需要了。

二、Attributes

正如基础语法中所示,Property提供了一组修饰符来描述属性,确切地说是用来指导编译器如何生成Getter/Setter方法。这组修饰符包括:

  • 用于线程: atomic(默认)、nonatomic
  • 用于内存: strong|retain(默认)、weak|unsafe_unretainedassign(默认)、copy
  • 用于访问: readwrite(默认)、readonly
  • 用于别名: getter=<new_getter_name>setter=<new_setter_name>

我将这组修饰符细分为了四个组,每个组内的修饰符之间是互斥的,各个组之间则是相容的

1、用于线程的修饰符

  • atomic

保证对属性的操作是线程安全的。

这是默认的用于描述线程操作相关的修饰符,即如果你不指定该属性是atomic还是nonatomic,那么编译器将按照aomic来合成getter/setter方法。atomic的意思是原子,即对该属性的操作会是原子操作,编译器会生成一些代码(例如加锁)来保证对该属性的访问是线程安全的。例如当某个对象在访问该属性的getter方法时,任何其他对象都无法在同一时刻访问它的setter方法,而只能等待,所以atomic在时间效率上要相对低(慢)一些。

  • nonatomic

对属性的操作是非线程安全的。

不保证线程安全意味着没有加锁,各个对象可以同时访问这个属性。一个对象在访问该属性的getter或者setter时另外的对象也可以同时访问该属性的getter或者setter,并不需要等待,所以在时间效率上要相对高(快)一些。

2、用于内存管理的修饰符

这一组为啥有三个默认?其实不是,对于指针类型的属性,默认会是strong或者retain,对于非指针类型则是assign。

  • retain / strong

在ARC中strong是作为retain的替代者出现,在MRR中strong和retain是一样的。如果未指定内存管理相关的修饰符,若属性是一个指向对象的指针,在启用ARC的情况下默认会是strong,非ARC下默认会是retain;若属性是一个原生类型如BOOL、int、CGFloat、NSInteger等类型,则默认是assign。

  • weak / unsafe_unretained

weak和strong是一同在ARC中引入,weak与strong不同的地方在于它不会增加引用计数,如果它修饰的属性所指向的对象由于引用计数为0而被释放时它将被设置为nil;unsafe_unretained与weak差不多,也是声明一个属性是弱引用,不会增加引用计数,但是当它修饰的属性指向的对象由于引用计数为0而被释放时,它不会被设置为nil。指向的对象被释放时不被设置为nil,不增加引用计数,这正符合它的名字:unsafe、unretained。

BTW: 如果要修饰一个局部变量为弱引用类型,可使用__weak,与之相反的则是__strong

  • assign

就是直接赋值。一般用于非指针原生类型所定义的属性。

可想而知既然是直接赋值而不同对象共同操作又不会引起问题,那么它应该是用于原生类型所定义的属性,所谓原生类型即非指针,例如C类型、BOOL、NSInteger、CGFloat等等。

  • copy

顾名思义,就是开辟新的内存空间Copy一份该对象。

一般用于可变(Mutable)对象,例如你有一个属性需要用来存储一个从别处来的可改写的字符串(NSMutableString)或者数组(NSMutableArray),你希望在你开始操作该属性时它的状态(如它的值)与你刚得到它时的状态一致(因为可变对象这期间可能会被其他对象操作),那么就应该在获取这个值时选择Copy一份而不是直接指向原有对象。一旦Copy,将会在开辟新的内存空间来存储它,它将只属于你一个人,对它的引用属于强引用,所以用完就需要负责对其进行释放。但如果开启了ARC,编译器就负责帮你release了。

3、用于控制访问的修饰符

  • readwrite

编译器将生成getter和setter方法。这是默认的修饰符。

  • readonly

编译器将只生成getter方法而不生成setter方法,以保证对外部只提供可读属性(事实上还是可以通过实例变量进行访问,但是这么不规范真的好吗?)。

对外不提供setter,但对内我们还是要修改属性的。可以在.h的@interface中定义属性,使用readonly修饰,在.m中使用类扩展来覆盖定义该属性,提供readwrite,这样就能实现对外提供只读而对内实现没有限制了。

4、用于取别名的修饰符

编译器合成的实例变量默认是在属性名称前加下划线_,默认的getter方法名称与属性名称相同,默认的setter方法名称则是属性名首字母大写然后在其前面加set来构成。这两个修饰符允许你修改默认生成的getter/setter方法名称。

  • getter=<new_getter_name>

指定新的getter方法名称。例如对于BOOL类型,你可能习惯在前面加is。如有个BOOL类型的属性名称为complete,可以修改它的getter为isComplete,而setter默认为setComplete。

  • setter=<new_setter_name>

指定新的setter方法名称。

参考资料

  1. 《The Objective-C 2.0 Programming Language》 - Chapter 4. Declared Properties
  2. @property and retain, assign, copy, nonatomic in Objective-C
  3. Objective-C declared @property attributes (nonatomic, copy, strong, weak)