iOS 属性关键字进阶篇

之前写过 《iOS属性关键字》基础篇,便于初学者理解,但没有从根本上去说明,属性的本质,这次就更进一步说明

属性的本质

 ivar + getter + setter
  • ivar 实例变量
  • 存取方法(access method = getter + setter)

直白一点说,每个属性本身就是一个实例的封装,getter 和 setter 方法,就是这个实例开放给外界的外部接口,
我们可以根据 getter 和 setter 方法,访问这个实例。

举个例子:

@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

runtime 中对属性的更新

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 就代表类型,可参阅 Type Encodings 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 方法中从偏移量开始取值 , 为了能够读取正确字节数,系统对象偏移量的指针类型进行了类型强转.

反过来推一推一个程序如何从空到有

这里为什么会用 “空” ,而不是 “无”,因为我们所有的创造都不是完全从无开始的,像一个人也不是从无到有的,一个人的诞生也是从空到有,必须有“空”,至于什么是 “空”,就不赘述。
一个程序的诞生,必须先要有内存空间,这个内存空间就是整个程序的原点,也是它的空。而如何让空的东西变成有,我们就要赋予它规则,有规则后,才能一生二,二生三,三生万物,这便是天地最根本的道理。而程序,也是遵循这个道理,更透彻的说,一段程序,就是承载一段有规律 0 和 1 的集合。

本质总是这样简单又复杂


结束语

实际上,这本身就是一种封装,便于开发者更简单实现,基础类的定义;这也是为什么 OC 代码的编写方式 和 C 代码编写方式的不一样的地方。

你可能感兴趣的:(iOS 属性关键字进阶篇)