Objective-C 属性 property 简介

status
category
date
summary
slug
icon
tags
password

属性是什么

苹果在Objective-C 2.0中引入了属性(property),它组合了新的预编译指令和新的访问控制。也就是说,使用了属性之后,系统会自动生成存取器,并且可以像Java一样通过点语法来访问属性。同时,开发者可以通过属性的关键词,去自定义属性点读写控制,例如只读等等。
存取器(accessor):用于获取或者设置属性变量的方法,即getter和setter方法。

从存取器说起

手动创建

如果我们手动创建存取器,代码会是这样:
声明了carNamecarType两个变量,并分别设置了settergetter方法。接下来看看对存取器的实现:
具体如何使用:
使用时,我们首先需要对对象car使用消息语法,给存取器发送消息,通过setter设置carNamecarType,然后通过getter分别获取它们。虽然这样的思路非常严谨,但代码过于繁琐,调用变量仍然需要发送消息。

使用属性@property

上面代码中,我们使用了@property创建了两个属性变量carNamecarType,我们先忽略nonatomicstrong是什么意思,然后实现这两个方法:
在.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,编译器会自动帮你加上gettersetter方法的实现,并且默认会去访问_carNamge这个成员变量,如果找不到_carNamge这个成员变量,会自动生成一个叫做_carNamge的私有成员变量。

属性的特性 Attribute

之前提到的nonatomicstrong,都叫做属性的特性。特性主要有三种类型,声明时最多可以写6个特性,每种类型都有默认值,接下来分别介绍一下。

原子性 Atomic

原子性指的是一个或多个操作,要么全部一次性执行,要么都不执行,中间不能被打断。也就是是否给存取器方法加锁。该类型有两个值可选:
  • atomic(默认值):具有原子性,如果有线程正在访问该变量,其他线程只能等待它访问完成之后才能访问,保证读写安全,但不保证线程安全
  • nonatomic:不具有原子性,读写不安全,线程不安全。
为什么原子性保证了读写安全,却不保证线程安全?例如,A线程进行写操作,这个过程中B线程插了一脚,进行了读操作,这时可以保证B读到的是A修改之后的值。但如果有多个线程在写,同时有一个线程在读,只能保证该线程读取到的值,是某一线程修改之后的完整的值,但不保证是哪一个。所以,atomicgettersetter加了锁,可以保证读取到的数据不是dirty的,即保证了读写方法的安全,但并不保证整个对象的线程安全。
这样一来,atomic显得非常鸡肋,既不能保证完全的线程安全,又需要额外的性能开销,所以一般来说,都会使用nonatomic

存取特性 Access

这个类型非常好理解,定义了属性是可读还是可读写,它有两个控制权限的值,和两个自定义方法名的值:
  • readwrite(默认):可读写,同时生成gettersetter
  • 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修饰,因为字符串常用于直接复制,而不是去引用某个字符串。所以说NSMutableStringNSMutableArray这些对象都不应该用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,则要重写settergetter其中之一,自己做判断,确保真正返回的值不是nil。否则报警告:Synthesized setter 'setName:' for null_resettable property 'name' does not handle nilUIView下面的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...

© 刘口子 2018-2025