Objective-C 属性 property 简介
status
category
date
summary
slug
icon
tags
password
属性是什么
苹果在Objective-C 2.0中引入了属性(property),它组合了新的预编译指令和新的访问控制。也就是说,使用了属性之后,系统会自动生成存取器,并且可以像Java一样通过点语法来访问属性。同时,开发者可以通过属性的关键词,去自定义属性点读写控制,例如只读等等。
存取器(accessor):用于获取或者设置属性变量的方法,即getter和setter方法。
从存取器说起
手动创建
如果我们手动创建存取器,代码会是这样:
声明了
carName
和carType
两个变量,并分别设置了setter
和getter
方法。接下来看看对存取器的实现:具体如何使用:
使用时,我们首先需要对对象
car
使用消息语法,给存取器发送消息,通过setter
设置carName
和carType
,然后通过getter
分别获取它们。虽然这样的思路非常严谨,但代码过于繁琐,调用变量仍然需要发送消息。使用属性@property
上面代码中,我们使用了
@property
创建了两个属性变量carName
和carType
,我们先忽略nonatomic
和strong
是什么意思,然后实现这两个方法:在.m文件中我们使用
@synthesize
自动生成这两个实例变量的存取器,并且隐藏了存取器,虽然我们看不到存取器,但它们确实是存在的,具体使用:可以看到,这时我们可以像Java一样通过点符号(.)来设置和访问变量。如果点表达式出现在等号(=)左边,则调用
setter
方法;如果点表达式出现在等号右边,则调用getter
方法。总体来说,使用属性
@property
创建变量,不仅可以省去声明存取器的大量重复代码,而且在实际调用时也会更加方便。当然,属性并不仅仅是为了偷懒而发明的,后面会介绍它控制变量的手段。@property和@synthesize
property属性:
- 作用:提供成员变量的访问方法的声明、控制成员变量的访问权限、控制多线程时成员变量的访问环境。
- 使用范围:property不但可以在interface,在协议protocol和类别category中也可以使用。
synthesize存取器方法:
- 作用:实现property所声明的方法的定义。
其实说直白就像是:property在头文件中声明了一些成员变量的访问方法,synthesize则在m文件中实现了由property声明的方法。
使用
@property
声明的变量,编译器会严格按照OC的方法命名规范去生成存取器,例如为carName
生成了setCarName
方法。此外,还会自动生成对应的实例变量名,由于自定义的变量名跟获取函数名一样,为了区分,实际的变量名在前面加下划线,如_carName
。另外虽然默认是加下划线,但可以在实现文件中使用关键词@synthesize
自定义实际的变量名:之后,就不能通过
_carName
来访问了,而需要使用_name
。在Xcode 4.5及以后的版本中,可以省略
@synthesize
,编译器会自动帮你加上getter
和setter
方法的实现,并且默认会去访问_carNamge
这个成员变量,如果找不到_carNamge
这个成员变量,会自动生成一个叫做_carNamge
的私有成员变量。属性的特性 Attribute
之前提到的
nonatomic
和strong
,都叫做属性的特性。特性主要有三种类型,声明时最多可以写6个特性,每种类型都有默认值,接下来分别介绍一下。原子性 Atomic
原子性指的是一个或多个操作,要么全部一次性执行,要么都不执行,中间不能被打断。也就是是否给存取器方法加锁。该类型有两个值可选:
atomic
(默认值):具有原子性,如果有线程正在访问该变量,其他线程只能等待它访问完成之后才能访问,保证读写安全,但不保证线程安全。
nonatomic
:不具有原子性,读写不安全,线程不安全。
为什么原子性保证了读写安全,却不保证线程安全?例如,A线程进行写操作,这个过程中B线程插了一脚,进行了读操作,这时可以保证B读到的是A修改之后的值。但如果有多个线程在写,同时有一个线程在读,只能保证该线程读取到的值,是某一线程修改之后的完整的值,但不保证是哪一个。所以,
atomic
为getter
和setter
加了锁,可以保证读取到的数据不是dirty
的,即保证了读写方法的安全,但并不保证整个对象的线程安全。这样一来,
atomic
显得非常鸡肋,既不能保证完全的线程安全,又需要额外的性能开销,所以一般来说,都会使用nonatomic
。存取特性 Access
这个类型非常好理解,定义了属性是可读还是可读写,它有两个控制权限的值,和两个自定义方法名的值:
readwrite
(默认):可读写,同时生成getter
和setter
。
readonly
:可读,不可写,只生成getter
。
getter
setter
有时为了明确语义,需要自定义存取器的名字,例如:
注意
setter
的方法名需要以冒号(:)结束内存管理 Storage
OC使用了ARC的内存管理机制,对象或者属性的是否可回收由引用数决定,所以引用的关系非常重要,这个特性提供了5个值:
assign
(基本类型默认):直接简单赋值,主要用于修饰基本类型和弱引用,计数器不会增加。在非ARC下默认都是assign。
strong
(对象默认):表示引用、持有这个对象,负责这个对象的生命周期,即常说的强引用。注意在ARC下对象默认是strong,基本类型的默认值是assign。
weak
:和assign
类似,表示持有一个引用指向该对象,没有主张权,也不会增加强引用计数,即常说的弱引用。如果一个对象被销毁,使用weak
时指向该对象的引用都会被设置为nil,但使用assign
时则不会。另外,常常使用weak
来解决循环引用的问题。
copy
:建立一个和原对象内容相同且索引计数为1的新对象,指针指向这个新对象,然后释放指针之前指向的旧对象。NSString变量一般都用copy修饰,因为字符串常用于直接复制,而不是去引用某个字符串。所以说NSMutableString
,NSMutableArray
这些对象都不应该用copy
修饰。
retain
:常用于引用类型,是为了持有对象,声明强引用,与strong相似。将指针本来指向的旧的引用对象释放掉,然后将指针指向新的引用对象,同时将新对象的索引计数加1。ARC出现之后已经不再常用。
可空性 Nullability
为了更好地和Swift混编,也就是支持Swift的optional可选类型,Objective-C新增了Nullability这个特性,具体有4个值:
nullable
:对象可为空
nonnull
:对象不可为空
null_unspecified
(默认):未指定
null_resettable
:稍微有点复杂,具体来说,就是调用setter
去reset属性时,可以传入nil,但通过getter
获取到的值不为空。
如果设置为
null_resettable
,则要重写setter
或getter
其中之一,自己做判断,确保真正返回的值不是nil。否则报警告:Synthesized setter 'setName:' for null_resettable property 'name' does not handle nil
。UIView
下面的tintColor
,就是null_resettable
。这样就保证,即使赋值为nil,也会返回一个非空的值。所以上面说到的,一个声明最多包含6个特性的写法为:
总结
属性是一个编译器指令,从程序员角度来说,我们需要通过特性来指定属性变量的使用场景,以便告诉编译器进行一些操作,将具体使用总结为下面的表格:
类别 | 关键字 | 描述 |
原子性 | atomic(默认) | 原子性,但不保证线程安全 |
ㅤ | nonatomic | 非原子性 |
读写权限 | readwrite(默认) | 可读可写 |
ㅤ | readonly | 只可读 |
修改方法名 | getter | 修改默认方法名 |
ㅤ | setter | 修改默认方法名,需要冒号 |
赋值操作 | assign(MRC默认) | 弱引用,直接赋值 |
ㅤ | strong(ARC默认) | 强引用,持有对象 |
ㅤ | weak | 弱引用,不持有对象 |
ㅤ | copy | 深拷贝副本 |
ㅤ | retain | 保留持有对象,仅MRC |
可空性 | nullable | 可空 |
ㅤ | nonnull | 不可空 |
ㅤ | null_unspecified(默认) | 未指定 |
ㅤ | null_resettable | 可置空,但不生效,需要重写 |
参考:《Objective-C基础教程》
Loading...