寫完類與對象后,我們今天來講講成員變量和屬性。
1.類型編碼
? ? ? ?編譯器將每個方法的返回值和參數(shù)類型編碼為一個字符串,并將其與方法的selector關(guān)聯(lián)在一起。當(dāng)給定一個類型時,@encode返回這個類型的字符串編碼。這些類型可以是諸如int、指針這樣的基本類型,也可以是結(jié)構(gòu)體、類等類型。事實上,任何可以作為sizeof()操作參數(shù)的類型都可以用于@encode()。對于屬性而言,還會有一些特殊的類型編碼,以表明屬性是只讀、拷貝、retain等等。(原文出自這里)
2.成員變量
打開runtime.h,我們一點都不驚訝地發(fā)現(xiàn)成員變量實際上是一個指向objc_ivar的指針。

繼續(xù)跟蹤objc_ivar,找到如下定義

故名思義,ivar_name就是成員變量的名稱,ivar_type就是類型,offset就是離基地址的偏移量。至于space是什么我不太清楚,猜測應(yīng)該是為兼容架構(gòu)而產(chǎn)生的變量。
3.屬性
objc_property_t:聲明的屬性的類型,是一個指向objc_property結(jié)構(gòu)體的指針。

property_getAttributes函數(shù)返回objc_property_attribute_t結(jié)構(gòu)體列表,objc_property_attribute_t結(jié)構(gòu)體包含name和value。下面的截圖是一些關(guān)于屬性和成員變量的操作,大同小異。

4.存取器
對于一個屬性來說,我們既可以直接訪問實例變量,也可以通過存取器間接訪問。那什么時候直接訪問,什么時候間接訪問。我這里列舉了一些使用存取器的情況。
?(1)鍵值觀察。存取器會自動調(diào)用willChangeValueForKey:和didChangeValueForKey:。如果直接訪問實例變量,會導(dǎo)致監(jiān)聽者收不到回調(diào)。
?(2)副作用。開發(fā)者在自身或者某個子類會在set方法中引入副作用??赡苁前l(fā)出通知火災(zāi)NSUndoManager中注冊了事件。同時,開發(fā)者或者子類會在get方法中增加緩存,而直接訪問實例變量會繞開緩存。
(3)惰性初始化。不用存取器的話,不會被初始化。
以下情況則是需要直接訪問實例變量:
(1)存取器內(nèi)部。有可能導(dǎo)致死循環(huán)。
(2)dealloc。這里使用存取器訪問的話有可能導(dǎo)致監(jiān)聽者收到不必要的通知。
(3)初始化。類似dealloc。這里通常只初始化readonly對象。
5.健壯的實例變量
由于我有一份ppt,這里就直接上ppt截圖,不一一講了。


6.一些疑問
有時候我們看到這樣的一個方法:class_addIvar(<#__unsafe_unretained Class cls#>, <#const char *name#>, <#size_t size#>, <#uint8_t alignment#>, <#const char *types#>),這個方法顯然是向一個類中添加成員變量,但是僅僅是能向一個動態(tài)創(chuàng)建的類中添加成員變量,而編譯好的類是不能再通過這個方法添加了。動態(tài)添加成員變量的時候,我們必須為它指定內(nèi)存管理策略,在這個方面里面卻看不見類似的參數(shù),那究竟該怎么做呢?詳情請看這里。
好了,寫完收工。