2009-10-24 116 views
18

我可以攔截Objective-C中的方法調用嗎?怎麼樣?攔截方法調用Objective-C

編輯: 馬克鮑威爾的回答給了我一個局部的解決方案,-forwardInvocation方法。 但是,文檔指出-forwardInvocation僅在對象發送的消息中沒有對應的方法時才被調用。我希望在任何情況下都可以調用某個方法,即使接收方確實有該選擇器。

回答

22

您可以通過調整方法調用來實現。假設你想抓住所有版本中NSTableView的:

static IMP gOriginalRelease = nil; 
static void newNSTableViewRelease(id self, SEL releaseSelector, ...) { 
    NSLog(@"Release called on an NSTableView"); 
    gOriginalRelease(self, releaseSelector); 
} 


    //Then somewhere do this: 
    gOriginalRelease = class_replaceMethod([NSTableView class], @selector(release), newNSTableViewRelease, "[email protected]:"); 

您可以在目標C運行時documentation獲得更多的細節。

+0

你可能是指@選擇器(發佈),而不是@選擇器(dealloc) – 2009-10-30 15:58:10

+0

是的,我的意思是@選擇器(發佈)。這個我很可愛,並且從一些調試dealloc的代碼中粘貼出來,但不想在示例中展示,因爲這樣做有一些特殊問題。固定。 – 2009-10-30 18:43:49

+0

@LouisGerbarg你可以發佈一個鏈接到您的原始來源,發佈swizzled? – funroll 2012-11-07 15:49:38

-1

要在調用方法時執行某些操作,可以嘗試基於事件的方法。所以當這個方法被調用時,它會廣播一個事件,這個事件被任何聽衆挑選出來。我用客觀的C來說並不是很棒,但我在Cocoa中用NSNotificationCenter找到了類似的東西。

但是如果通過「攔截」你的意思是「停止」,那麼也許你需要更多的邏輯來決定是否應該調用該方法。

+2

我認爲他指的是元編程技術。 – Geo 2009-10-24 16:45:19

1

也許你想要NSObject-forwardInvocation方法。這使您可以捕獲消息,重新定位它,然後重新發送。

+2

該文檔指出* -forwardInvocation *僅在對象發送消息時沒有相應的方法時才調用。我希望在任何情況下都可以調用某個方法,即使接收方確實有該選擇器。 – luvieere 2009-10-24 17:01:17

+1

這一評論比你原來的問題顯著的詳細信息...;) – MarkPowell 2009-10-24 17:25:39

+0

我會編輯,感謝您指出。 – luvieere 2009-10-24 17:26:31

0

方法調用,no。發送消息,是的,但是如果你想得到一個好的答案,你將不得不更多地描述。

+2

Obj-C中的方法調用的術語是「消息發送」,因此它們是一個,它們是相同的,而不是一個函數調用 - 你是正確的 - 不能被動態攔截。 – 2013-06-21 19:54:02

1

你可以用你自己的方法調用方法調用,它可以做任何你想做的「截取」操作,並調用原始實現。 Swizzling通過class_replaceMethod()完成。

15

攔截方法在Objective-C調用(asuming它是一個Objective-C,不是C呼叫)與被叫方法混寫技術完成。

你可以找到關於如何實現here介紹。例如,在真實項目中如何實現方法調配,請查看OCMock(Objective-C的隔離框架)。

11

發送在Objective-C消息被轉換成函數objc_msgSend(receiver, selector, arguments)或其變體objc_msgSendSuperobjc_msgSend_stretobjc_msgSendSuper_stret中的一個的呼叫。

如果可以更改這些函數的實現,我們可以攔截任何消息。不幸的是,objc_msgSend是Objective-C運行時的一部分,不能被覆蓋。

通過Google搜索,我在Google圖書上找到一篇論文:A Reflective Architecture for Process Control Applications by Charlotte Pii Lunau。本文通過將對象的isa類指針重定向到自定義MetaObject類的實例來介紹一種黑客攻擊。因此,用於修改對象的消息被髮送到MetaObject實例。由於MetaObject類沒有自己的方法,因此它可以通過將消息轉發給修改後的對象來響應前向調用。

該論文不包含源代碼的有趣部分,我不知道這種方法是否會在可可中產生副作用。但嘗試可能很有趣。

2

創建的NSProxy一個子類,並實現-forwardInvocation:-methodSignatureForSelector:(或-forwardingTargetForSelector:,如果你只是指揮它到第二個對象,而不是用自己的方法擺弄)。

NSProxy是設計用於實現-forwardInvocation:類。它有幾種方法,但大多數你不希望它們被捕獲。例如,捕獲引用計數方法將阻止代理被解除分配,除非在垃圾回收下。但是,如果有上NSProxy,你絕對需要提出具體方法,可以專門重寫方法和手動調用-forwardInvocation:。這樣做僅僅是因爲一個方法是NSProxy文檔中列出並不意味着NSProxy實現它。注意,僅僅是預期,所有的代理對象有它。

如果這不會爲你工作,提供有關情況的其他詳細信息。

5

如果你想記錄的消息從應用程序代碼發送的-forwardingTargetForSelector:尖是解決方案的一部分。
包裝你的對象:

@interface Interceptor : NSObject 
@property (nonatomic, retain) id interceptedTarget; 
@end 

@implementation Interceptor 
@synthesize interceptedTarget=_interceptedTarget; 

- (void)dealloc { 
    [_interceptedTarget release]; 
    [super dealloc]; 
} 

- (id)forwardingTargetForSelector:(SEL)aSelector { 
    NSLog(@"Intercepting %@", NSStringFromSelector(aSelector)); 
    return self.interceptedTarget; 
} 

@end 

現在做這樣的事情:

Interceptor *i = [[[Interceptor alloc] init] autorelease]; 
NSFetchedResultsController *controller = [self setupFetchedResultsController]; 
i.interceptedTarget = controller; 
controller = (NSFetchedResultsController *)i; 

和消息發送,你將有一個日誌。請注意,截取的對象內發送的發送不會被攔截,因爲它們將使用原始對象的「自我」指針發送。

3

如果你只是想記錄從外部調用的消息(通常代表呼籲,看看哪些類型的消息的時候,等),可以覆蓋respondsToSelector這樣的:

- (BOOL)respondsToSelector:(SEL)aSelector { 
NSLog(@"respondsToSelector called for '%@'", NSStringFromSelector(aSelector)); 

// look up, if a method is implemented 
if([[self class] instancesRespondToSelector:aSelector]) return YES; 

return NO; 

}