Objective C的方法替換

2021-06-24 11:46:08 字數 2351 閱讀 9301

本文將要討論objective-c中的方法替換(method replacement)和swizzling(移魂**)。

overriding methods在任何物件導向語言中都很常見,主要用於子類化中。在子類中複寫乙個方法,然後在子類的例項就可以使用這個被重寫的方法。

對於乙個你無法控制其實例化(instantiation)的類,有時你或許會想複寫它的某個方法,雖然有點瘋狂。子類化可做不到,因為你沒有機會子類化你的子類。

posing是個很有趣的技術,不過已經過時了,因為64位和iphone環境下的objective-c runtime中不再支援它了. 通過這個偽裝(posing),你可子類化,然後將這個子類偽裝成它的父類。像變魔術一般,runtime會讓這個子類應用於各處,這時方法複寫又有了用處。既然被拋棄了,也就不必多費口舌了。

使用歸類(category)的技術,可以方便地為乙個已經存在的類複寫其方法:

@implementation

nsview

(myoverride)

-(void

)drawrect:

(nsrect)r

@end

這種方法其實僅僅適用於複寫目標類的父類中實現的函式。如果直接複寫目標類中的方法,使用歸類會帶來兩個問題:

它無法呼叫方法的之前的實現。替換掉後,之前的實現就被完全改寫了。但大部分情況下,只是想增加些功能,並不期望完全替代。

如果被多個category複寫,執行時(runtime)並不保證哪個真正會被使用到。

使用乙個稱為swizzling的技術,可以為歸類(category)解決上面兩個問題,既可以呼叫舊的實現,又可以避免多個category帶來的不確定性。它的秘訣是使用乙個不同的函式名來複寫,然後由執行時(runtime)交換它們。

首先,用乙個不同的名字複寫:

@implementation

nsview

(myoverride)

-(void

)override_drawrect:

(nsrect)r

@end

(譯註:呵呵,不知道你是不是和我一樣,初次看到**還以為是個遞迴呼叫呢。) 其實是這個新的方法在執行時已經和原先的函式對調了(現在還沒做到,往下看!)。在執行時,呼叫 override_drawrect: 方法其實就是呼叫舊的實現。

接下來,你還要寫些**才能完成交換:

void

methodswizzle

(class c,

selorigsel,

seloverridesel)

(譯註:addmethod會讓目標類的方法指向新的實現,使用replacemethod再將新的方法指向原先的實現,這樣就完成了交換操作。)

如果新增失敗了,就是第二情況(在目標類重寫的方法)。這時可以通過method_exchangeimplementations來完成交換:

else

} 對於第二種情況,因為class_getinstancemethod 會返回父類的實現,如果直接替換,就會替換掉父類的實現,而不是目標類中的實現。(詳細的函式說明在)

舉個具體的例子, 假設要替換掉-[nsview description]. 如果nsview 沒有實現-description (可選的) 那你就可會得到nsobject的方法。如果呼叫method_exchangeimplementations , 你就會把nsobject 的方法替換成你的**。這應該不會是你想要的吧?

最後在乙個合適位置呼叫一下就可以了。比如在乙個+load 方法中呼叫: +

(void

)load

前面的內容確實有些難懂。swizzling的概念的確顯得有些古怪,特別是在函式中轉來轉去的,多少讓人有些思維扭曲的感覺。我下面要介紹乙個更為簡潔,也更容易理解和實現的方式。

這種方式不再需要儲存舊有的方法,也不必動態的區分[self override_drawrect: r] 。我們從頭實現。

相對於將原有的方法存放於乙個新的方法中,這裡使用乙個全域性指標來儲存:

void(*

gorigdrawrect)(id,

sel, nsrect);

然後在+load 裡賦值: +

(void

)load

當然,這個方法不是那麼優雅,不過我認為它更易於運用。

複寫不是你自家的類是危險的! 盡量避免這麼做,要不然就盡最大的可能細心處理。

fridayqa,2010-01-29, method replacement for fun and profit

參考: 

Objective c 方法的呼叫

在書寫了類的宣告和實現後,應用程式如何去呼叫它呢?在objective c中,呼叫方法的簡單格式如下 1 例項 方法 如 person setage 32 其中 person是person類的例項。或者是 2 類名 方法名 如 nsstring str nsdate date 這是直接呼叫類nsda...

Objective c 方法的呼叫

在書寫了類的宣告和實現後,應用程式如何去呼叫它呢?在objective c中,呼叫方法的簡單格式如下 1 例項 方法 如 person setage 32 其中 person是person類的例項。或者是 2 類名 方法名 如 nsstring str nsdate date 這是直接呼叫類nsda...

Objective C構造方法

構造方法 用來初始化的方法 之前我們建立物件的方式一直是使用 new 但是使用new建立的物件,都是給我們預設做了初始化的。有的時候,我們需要按照我們自己的需求進行初始化,那麼怎麼做呢 首先了解一下,我們使用new建立物件的時候,都做了什麼事情。分配儲存空間 alloc 呼叫類方法alloc來給物件...