It's our wits that make us men.

property、synthesize

Posted on By Hanchun Liu

@property 的本质是什么?

“属性” (property)主要的作用就在于封装对象中的数据

编译器自动生成一套读取方法,用以访问该变量。

@property = setter + getter

例如下面这个类:

@interface Person : NSObject
@property NSString *firstName;
@property NSString *lastName;
@end

上述代码写出来的类与下面这种写法等效:

@interface Person : NSObject
- (NSString *)firstName;
- (void)setFirstName:(NSString *)firstName;
- (NSString *)lastName;
- (void)setLastName:(NSString *)lastName;
@end

property在runtime中是objc_property_t定义如下:

typedef struct objc_property *objc_property_t; 而objc_property是一个结构体,包括name和attributes,定义如下:

struct property_t {
const char *name;
const char *attributes;
};

而attributes本质是objc_property_attribute_t,定义了property的一些属性,定义如下:

/// Defines a property attribute
typedef struct {
const char *name;           /**< The name of the attribute */
const char *value;          /**< The value of the attribute (usually empty) */
} objc_property_attribute_t;

而attributes的具体内容是什么呢?其实,包括:类型,原子性,内存语义和对应的实例变量。

例如:我们定义一个string的

property@property (nonatomic, copy) NSString *string;

通过 property_getAttributes(property)获取到attributes并打印出来之后的结果为T@”NSString”,C,N,V_string

其中T就代表类型,C就代表Copy,N代表nonatomic,V就代表对于的实例变量。

ivar、getter、setter 是如何生成并添加到这个类中的?

“自动合成”( autosynthesis)

完成属性定义后,编译器会自动编写访问这些属性所需的方法,此过程叫做“自动合成”(autosynthesis)。

需要强调的是,这个过程由编译器在编译期执行,所以编辑器里看不到这些“合成方法”(synthesized method)的源代码。除了生成方法代码 getter、setter 之外,编译器还要自动向类中添加适当类型的实例变量,并且在属性名前面加下划线,以此作为实例变量的名字。在前例中,会生成两个实例变量,其名称分别为 _firstName 与 _lastName。也可以在类的实现代码里通过 @synthesize 语法来指定实例变量的名字.

@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end

他大致生成了五个东西

OBJC_IVAR_$类名$属性名称 :该属性的“偏移量” (offset),这个偏移量是“硬编码” (hardcode),表示该变量距离存放对象的内存区域的起始地址有多远。
setter 与 getter 方法对应的实现函数
ivar_list :成员变量列表
method_list :方法列表
prop_list :属性列表

也就是说我们每次在增加一个属性,系统都会在 ivar_list 中添加一个成员变量的描述,在 method_list 中增加 setter 与 getter 方法的描述,在属性列表中增加一个属性的描述,然后计算该属性在对象中的偏移量,然后给出 setter 与 getter 方法对应的实现,在 setter 方法中从偏移量的位置开始赋值,在 getter 方法中从偏移量开始取值,为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.

属性可以拥有的特质分为四类:

1、原子性--- nonatomic 特质

在默认情况下,由编译器合成的方法会通过锁定机制确保其原子性(atomicity)。如果属性具备 nonatomic 特质,则不使用自旋锁。请注意,尽管没有名为“atomic”的特质(如果某属性不具备 nonatomic 特质,那它就是“原子的” ( atomic) ),但是仍然可以在属性特质中写明这一点,编译器不会报错。若是自己定义存取方法,那么就应该遵从与属性特质相符的原子性。

2、读/写权限---readwrite(读写)、readonly (只读)

3、内存管理语义---assign、strong、 weak、unsafe_unretained、copy

4、方法名---getter=<name> 、setter=<name>

总结下 @synthesize 合成实例变量的规则,有以下几点:

1、如果指定了成员变量的名称,会生成一个指定的名称的成员变量:

@synthesize firstName = _myFirstName;

2、如果这个成员已经存在了就不再生成了.

3、如果是 @synthesize foo; 还会生成一个名称为foo的成员变量,也就是说:

如果没有指定成员变量的名称会自动生成一个属性同名的成员变量

4、@synthesize foo = _foo; 就不会生成成员变量了.

什么情况下不会autosynthesis(自动合成)?

.同时重写了 setter 和 getter 时

.重写了只读属性的 getter 时

.使用了 @dynamic 时

.在 @protocol 中定义的所有属性

.在 category 中定义的所有属性

.重载的属性