之前寫過 《iOS屬性關(guān)鍵字》基礎(chǔ)篇,便于初學(xué)者理解,但沒有從根本上去說明,屬性的本質(zhì),這次就更進一步說明
屬性的本質(zhì)
ivar + getter + setter
- ivar 實例變量
- 存取方法(access method = getter + setter)
直白一點說,每個屬性本身就是一個實例的封裝,getter 和 setter 方法,就是這個實例開放給外界的外部接口,
我們可以根據(jù) 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 是一個結(jié)構(gòu)體,包括 name 和 attributes,定義如下:
struct property_t {
const char *name;
const char *attributes;
};
這這其中 attributes 本質(zhì)是 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 的具體內(nèi)容是什么呢?其實,包括:類型,原子性,內(nèi)存語義和對應(yīng)的實例變量。
例如:
我們定義一個 string 的 property @property (nonatomic, copy) NSString *string;,
通過 property_getAttributes(property)獲取到 attributes 并打印出來之后的結(jié)果為 T@"NSString",C,N,V_string
其中 T 就代表類型,可參閱 Type Encodings C 就代表 Copy,N 代表 nonatomic,V 就代表對于的實例變量。
ivar、getter、setter 是如何生成并添加到這個類中的?
“自動合成”( autosynthesis)
完成屬性定義后,編譯器會自動編寫訪問這些屬性所需的方法,此過程叫做“自動合成”( autosynthesis )。需要強調(diào)的是,這個過程由編譯器在編譯期執(zhí)行,所以編輯器里看不到這些“合成方法”( synthesized method )的源代碼。除了生成方法代碼 getter、setter 之外,編譯器還要自動向類中添加適當(dāng)類型的實例變量,并且在屬性名前面加下劃線,以此作為實例變量的名字。在前例中,會生成兩個實例變量,其名稱分別為 _firstName 與 _lastName。也可以在類的實現(xiàn)代碼里通過 @synthesize 語法來指定實例變量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
其實相關(guān)的代碼,會大致生成了五個東西
- OBJC_IVAR_$類名$屬性名稱 :該屬性的“偏移量” (offset),這個偏移量是“硬編碼” (hardcode),表示該變量距離存放對象的內(nèi)存區(qū)域的起始地址有多遠。
- setter 與 getter 方法對應(yīng)的實現(xiàn)函數(shù)
- ivar_list :成員變量列表
- method_list :方法列表
- prop_list :屬性列表
我們每次在增加一個屬性:
- 系統(tǒng)首先會在 ivar_list 中添加一個成員變量的描述
- 在 method_list 中增加 setter 與 getter 方法的描述,
- 在屬性列表中增加一個屬性的描述
然后計算該屬性在對象中的偏移量,然后給出 setter 與 getter 方法對應(yīng)的實現(xiàn),在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值 , 為了能夠讀取正確字節(jié)數(shù),系統(tǒng)對象偏移量的指針類型進行了類型強轉(zhuǎn).
反過來推一推一個程序如何從空到有
這里為什么會用 “空” ,而不是 “無”,因為我們所有的創(chuàng)造都不是完全從無開始的,像一個人也不是從無到有的,一個人的誕生也是從空到有,必須有“空”,至于什么是 “空”,就不贅述。
一個程序的誕生,必須先要有內(nèi)存空間,這個內(nèi)存空間就是整個程序的原點,也是它的空。而如何讓空的東西變成有,我們就要賦予它規(guī)則,有規(guī)則后,才能一生二,二生三,三生萬物,這便是天地最根本的道理。而程序,也是遵循這個道理,更透徹的說,一段程序,就是承載一段有規(guī)律 0 和 1 的集合。
??本質(zhì)總是這樣簡單又復(fù)雜
結(jié)束語
實際上,這本身就是一種封裝,便于開發(fā)者更簡單實現(xiàn),基礎(chǔ)類的定義;這也是為什么 OC 代碼的編寫方式 和 C 代碼編寫方式的不一樣的地方。