之前寫過 《iOS屬性關(guān)鍵字》基礎(chǔ)篇芙沥,便于初學(xué)者理解薄嫡,但沒有從根本上去說明失仁,屬性的本質(zhì)胳泉,這次就更進(jìn)一步說明
屬性的本質(zhì)
ivar + getter + setter
- ivar 實(shí)例變量
- 存取方法(access method = getter + setter)
直白一點(diǎn)說拐叉,每個(gè)屬性本身就是一個(gè)實(shí)例的封裝,getter 和 setter 方法扇商,就是這個(gè)實(shí)例開放給外界的外部接口凤瘦,
我們可以根據(jù) getter 和 setter 方法,訪問這個(gè)實(shí)例钳吟。
舉個(gè)例子:
@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 中對(duì)屬性的更新
property 在 runtime 中是 objc_property_t 定義如下:
typedef struct objc_property *objc_property_t;
而 objc_property 是一個(gè)結(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)容是什么呢?其實(shí)暇番,包括:類型嗤放,原子性,內(nèi)存語義和對(duì)應(yīng)的實(shí)例變量壁酬。
例如:
我們定義一個(gè) 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 就代表對(duì)于的實(shí)例變量。
ivar希俩、getter吊宋、setter 是如何生成并添加到這個(gè)類中的?
“自動(dòng)合成”( autosynthesis)
完成屬性定義后,編譯器會(huì)自動(dòng)編寫訪問這些屬性所需的方法颜武,此過程叫做“自動(dòng)合成”( autosynthesis )璃搜。需要強(qiáng)調(diào)的是,這個(gè)過程由編譯器在編譯期執(zhí)行鳞上,所以編輯器里看不到這些“合成方法”( synthesized method )的源代碼这吻。除了生成方法代碼 getter、setter 之外篙议,編譯器還要自動(dòng)向類中添加適當(dāng)類型的實(shí)例變量唾糯,并且在屬性名前面加下劃線,以此作為實(shí)例變量的名字。在前例中移怯,會(huì)生成兩個(gè)實(shí)例變量拒名,其名稱分別為 _firstName 與 _lastName。也可以在類的實(shí)現(xiàn)代碼里通過 @synthesize 語法來指定實(shí)例變量的名字.
@implementation Person
@synthesize firstName = _myFirstName;
@synthesize lastName = _myLastName;
@end
其實(shí)相關(guān)的代碼,會(huì)大致生成了五個(gè)東西
- OBJC_IVAR_$類名$屬性名稱 :該屬性的“偏移量” (offset)芋酌,這個(gè)偏移量是“硬編碼” (hardcode),表示該變量距離存放對(duì)象的內(nèi)存區(qū)域的起始地址有多遠(yuǎn)雁佳。
- setter 與 getter 方法對(duì)應(yīng)的實(shí)現(xiàn)函數(shù)
- ivar_list :成員變量列表
- method_list :方法列表
- prop_list :屬性列表
我們每次在增加一個(gè)屬性:
- 系統(tǒng)首先會(huì)在 ivar_list 中添加一個(gè)成員變量的描述
- 在 method_list 中增加 setter 與 getter 方法的描述,
- 在屬性列表中增加一個(gè)屬性的描述
然后計(jì)算該屬性在對(duì)象中的偏移量,然后給出 setter 與 getter 方法對(duì)應(yīng)的實(shí)現(xiàn),在 setter 方法中從偏移量的位置開始賦值,在 getter 方法中從偏移量開始取值 , 為了能夠讀取正確字節(jié)數(shù),系統(tǒng)對(duì)象偏移量的指針類型進(jìn)行了類型強(qiáng)轉(zhuǎn).
反過來推一推一個(gè)程序如何從空到有
這里為什么會(huì)用 “空” 脐帝,而不是 “無”,因?yàn)槲覀兯械膭?chuàng)造都不是完全從無開始的糖权,像一個(gè)人也不是從無到有的堵腹,一個(gè)人的誕生也是從空到有,必須有“空”星澳,至于什么是 “空”疚顷,就不贅述。
一個(gè)程序的誕生禁偎,必須先要有內(nèi)存空間腿堤,這個(gè)內(nèi)存空間就是整個(gè)程序的原點(diǎn),也是它的空如暖。而如何讓空的東西變成有笆檀,我們就要賦予它規(guī)則,有規(guī)則后盒至,才能一生二酗洒,二生三,三生萬物枷遂,這便是天地最根本的道理樱衷。而程序,也是遵循這個(gè)道理酒唉,更透徹的說矩桂,一段程序,就是承載一段有規(guī)律 0 和 1 的集合黔州。
??本質(zhì)總是這樣簡單又復(fù)雜
結(jié)束語
實(shí)際上耍鬓,這本身就是一種封裝,便于開發(fā)者更簡單實(shí)現(xiàn)流妻,基礎(chǔ)類的定義牲蜀;這也是為什么 OC 代碼的編寫方式 和 C 代碼編寫方式的不一樣的地方。