返回 / Index

2017·10·17 · 阅读约 11 分钟 · Technical

Objective-C属性(property)的特性(attribute)

更新于

先以一图总结:

Atttributes

以下:「attribute(s)」,「特性」是指同一事物(都指@property后面括号内的单词)。

用Objective-C做过开发的朋友都知道,类里面的属性(可以近似地理解为类的变量)是用@property关键字定义的,然后@property后面的括号,会写上若干「特性(attribute)」,后面跟数据类型、属性名称。如:

@property (copy, nonatomic) NSString *name;

写OC良久,对括号内的这些attributes,还是一知半解、不知其然,亦不知其所以然。用的时候就照葫芦画瓢。

现在大伙儿慢慢转向苹果的新开发语言Swift,似乎亦不必花太多时间在Objective-C上。

不过那种一知半解,不明就里的感觉,有点如鲠在喉,不甚舒服,所以花了点时间,research了一番:

为什么要有@property?

要搞清楚「特性」,先搞清楚@property,为什么要有@property

在2006年的WWDC大会上,苹果发布了Objective-C 2.0,其中就包括**Properties这个新的语法,把原来的实例变量定义成Properties(属性)**。这个变化,和以前相比,有什么变化呢?

###Objective-C2.0之前:

没有Properties之前,定义实例变量,是这样的:

@interface Person : NSObject {
@public
    NSString *name;
@private
    int age;
}
@end

然后在.h文件,声明setter和getter方法(setter和getter统称「accessors/存取器/访问器」),再在.m文件实现setter和getter,这样就可以封装起来,供其他类访问(取值、赋值)了。

然而,即使不使用setter和getter,其他类也可以通过->来直接访问,如:

personA->name = @"123";

NSLog(@"personA->name:%@", personA->name);

为什么要getter和setter

那么,为什么还要如此麻烦地声明和实现setter和getter呢?主要基于三个原因(参考:Please explain Getter and Setters in Objective C):

因此,写getter和setter,可算是Objective-C中「约定俗成」的做法了。(Swift有类似的「Computed Properties/计算属性」)

所以,在没有Objective-C2.0@property之前,我们几乎需要为所有的实例变量,手动写getter和setter——听听就觉得很可怕,对不对?

###Objective-C2.0之后:

庆幸的是,程序员都喜欢「偷懒」,所以就有了2006年Objective-C2.0中的新语法:Properties

它帮我们自动生成getter和setter(声明方法,并实现方法。当然,这部分代码并不会出现在你的项目中,是隐藏起来的)。

不过,@property的写法,也经过数次变迁(新旧写法混在一起,就更让人困惑了):

所以,现在我们写@property声明属性,其实是做了三件事

这就是@property为我们所做的事情。

知道它为我们做了什么,自然也就能回答:「为什么要有@property?」这个问题了。

@property后面的括号又是怎么回事?

@property (copy, nonatomic) NSString *name;

这种写法,大家肯定都写过,不过,后面跟着的这个括号又是什么玩意儿呢?

官方把括号里面的东西,叫做「attribute/特性」。

先试一下,把括号里的两个单词都删掉,你会发现,还能正常工作。而事实上,以下两种写法,是等价的:

@property () NSString *name;// 或者@property NSString *name;
@property (atomic, strong, readwrite) NSString *name;

因为attribute主要有三种类型(实际上最多可以写6个特性,后面详述),每种类型都有默认值。如果什么都不写,系统就会取用默认值(看看,苹果良苦用心,偷偷帮我们做了那么多事情)。

如上所述,attributes有三种类型:

1.Atomicity(原子性)

比较简单的一句话理解就是:是否给setter和getter加锁(是否保证setter或者getter的每次访问是完整性的)。

原子性,有atomic和nonatomic两个值可选。默认值是atomic(也就是不写的话,默认是atomic)。

2.Access(存取特性)

存取特性有readwrite(默认值)和readonly

这个从名字看就很容易理解,定义了这个属性是「只读」,还是「读写」皆可。

如果是readwrite,就是告诉编译器,同时生成getter和setter。如果是readonly,只生成getter。

3.Storage(内存管理特性)(管理对象的生命周期的)

最常用到strongweakassigncopy4个attributes。(还有一个retain,不怎么用了)

所以,总结一下。

4.扩展

其实,除了上面3种经常用到的特性类型,还有2种不太见到。

存取方法不能以new开头,如果你要以new开头命名一个属性:@property (copy, nonatomic) NSString *newName;于是会默认生成一个new开头的getter方法:

这时候就会报错:Property follows Cocoa naming convention for returning 'owned' objects

解决办法,就是用**getter=**重命名getter方法:

@property (copy, nonatomic, getter=theNewName) NSString *newName;

所以!综上所述,attribute最多可以写6个进去:1.原子性、2.存取特性、3.内存管理特性、4.重命名getter、5.重命名setter,6.nullability:

@property (nonatomic, readonly, copy, getter=theNewTitle, setter=setTheNewTitle:, nullable) NSString *newTitle;

不过,应该没有谁闲得蛋疼会这样写的。

最短的写法就是什么都不写,连括号都可以不要:

@property BOOL isOpen;

毕。

Antony