在既有類中使用關聯物件存放自定義資料

2021-07-27 03:12:54 字數 2772 閱讀 8773

有時需要在物件中存放相關資訊。這時我們通常會從物件所屬的類中繼承乙個子類,然後改用這個子類物件。然而並非所有情況下都能這麼做,有時候類的例項可能是由某種機制所建立的,而開發者無法令這種機制建立出自己所寫的子類例項。objective-c中有一項強大的特性可以解決此問題,這就是「關聯物件」(associated object)。

可以給某物件關聯許多其他物件,這些物件通過「鍵」來區分。儲存物件值的時候,可以指明「儲存策略」(storage policy),用以維護相應的「記憶體管理語義」。儲存策略由名為objc_associationpolicy的列舉所定義,表2-1列出了該列舉的取值,同時還列出了與之等效的@property屬性:假如關聯物件成為了屬性,那麼它就會具備對應的語義(第6條詳解了「屬性」這個概念)。

表2-1 物件關聯型別

下列方法可以管理關聯物件:

void objc_setassociatedobject(id object, void*key, id value, objc_associationpolicy policy)

此方法以給定的鍵和策略為某物件設定關聯物件值。

id objc_getassociatedobject(id object, void*key)

此方法根據給定的鍵從某物件中獲取相應的關聯物件值。

void objc_removeassociatedobjects(id object)

此方法移除指定物件的全部關聯物件。

我們可以把某物件想象成nsdictionary,把關聯到該物件的值理解為字典中的條目,於是,訪問關聯物件的值就相當於在nsdictionary物件上呼叫[object setobject:value forkey:key]與[object objectforkey:key]方法。然而兩者之間有個重要差別:設定關聯物件時用的鍵(key)是個「不透明的指標」(opaque pointer)。如果在兩個鍵上呼叫「isequal:」方法的返回值是yes,那麼nsdictionary就認為二者相等;然而在設定關聯物件值時,若想令兩個鍵匹配到同乙個值,則二者必須是完全相同的指標才行。鑑於此,在設定關聯物件值時,通常使用靜態全域性變數做鍵。

關聯物件用法舉例

開發ios時經常用到uialertview類,該類提供了一種標準檢視,可向使用者展示警告資訊。當使用者按下按鈕關閉該檢視時,需要用委託協議(delegate protocol)來處理此動作,但是,要想設定好這個委託機制,就得把建立警告檢視和處理按鈕動作的**分開。由於**分作兩塊,所以讀起來有點亂。比方說,我們在使用uialertview時,一般都會這麼寫:

- (void)askuseraquestion   

// uialertviewdelegate protocol method

- (void)alertview:(uialertview *)alertview

clickedbuttonatindex:(nsinteger)buttonindex

else

}

如果想在同乙個類裡處理多個警告資訊檢視,那麼**就會變得更為複雜,我們必須在delegate方法中檢查傳入的alertview引數,並據此選用相應的邏輯。要是能在建立警告檢視的時候直接把處理每個按鈕的邏輯都寫好,那就簡單多了。這可以通過關聯物件來做。建立完警告檢視之後,設定乙個與之關聯的「塊」(block),等到執行delegate方法時再將其讀出來。此方案的實現**如下:

#import 

static

void *eocmyalertviewkey = "eocmyalertviewkey";

- (void)askuseraquestion else

};

objc_setassociatedobject(alert,

eocmyalertviewkey,

block,

o bjc_association_copy);

[alert show];

} // uialertviewdelegate protocol method

- (void)alertview:(uialertview*)alertview

clickedbuttonatindex:(nsinteger)buttonindex

以這種方式改寫之後,建立警告檢視與處理操作結果的**都放在一起了,這樣比原來更易讀懂,因為我們無須在兩部分**之間來回遊走,即可明白警告檢視的用處。但是,採用該方案時需注意:塊可能要捕獲(capture)某些變數,這也許會造成「保留環」(retain cycle)。第40條詳述了此問題。

正如大家所見,這種做法很有用,但是只應該在其他辦法行不通時才去考慮用它。若是濫用,則很快就會令**失控,使其難於除錯。「保留環」產生的原因很難查明,因為關聯物件之間的關係並沒有正式的定義(formal definition),其記憶體管理語義是在關聯的時候才定義的,而不是在介面中預先定好的。使用這種寫法時要小心,不能僅僅因為某處可以用該寫法就一定要用它。想建立這種uialertview還有個辦法,那就是從中繼承子類,把塊儲存為子類中的屬性。筆者認為:若是需要多次用到alert檢視,那麼這種做法比使用關聯物件要好。

要點可以通過「關聯物件」機制來把兩個物件連起來。

定義關聯物件時可指定記憶體管理語義,用以模仿定義屬性時所採用的「擁有關係」與「非擁有關係」。

只有在其他做法不可行時才應選用關聯物件,因為這種做法通常會引入難於查詢的bug。

第10條 在既有類中使用關聯物件存放自定義資料

本條要點 作者總結 有時需要在物件中存放相關資訊。這時我們通常從物件所屬的類中繼承乙個子類,然後改用這個子類物件。然而並非所有情況下都能這麼做,有時候類的例項可能是由某種機制所建立的,而開發者無法令這種機制建立出自己所寫的子類例項。objective c 中有一項強大的特性可以解決此問題,這就是 關...

在MFC中使用自定義視窗類

1 在 wndclass wc wc.cbcl tra 0 wc.cbwndextra 0 wc.hbrbackground hbrush getstockobject white brush wc.hcursor loadcursor idc arrow wc.hicon null wc.hins...

在ASP中使用類

vbscript5中增加了許多新功能,最振奮人心的當屬類和正規表示式的出現。以下是本人寫的乙個解析html 的類。我是 學php的,語法有不習慣的地方,請大家多包含。class htmlparse 設定 initialize 事件。private sub class initialize myglo...