我可以攔截Objective-C中的方法調用嗎?怎麼樣?攔截方法調用Objective-C
編輯: 馬克鮑威爾的回答給了我一個局部的解決方案,-forwardInvocation方法。 但是,文檔指出-forwardInvocation僅在對象發送的消息中沒有對應的方法時才被調用。我希望在任何情況下都可以調用某個方法,即使接收方確實有該選擇器。
我可以攔截Objective-C中的方法調用嗎?怎麼樣?攔截方法調用Objective-C
編輯: 馬克鮑威爾的回答給了我一個局部的解決方案,-forwardInvocation方法。 但是,文檔指出-forwardInvocation僅在對象發送的消息中沒有對應的方法時才被調用。我希望在任何情況下都可以調用某個方法,即使接收方確實有該選擇器。
您可以通過調整方法調用來實現。假設你想抓住所有版本中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獲得更多的細節。
要在調用方法時執行某些操作,可以嘗試基於事件的方法。所以當這個方法被調用時,它會廣播一個事件,這個事件被任何聽衆挑選出來。我用客觀的C來說並不是很棒,但我在Cocoa中用NSNotificationCenter找到了類似的東西。
但是如果通過「攔截」你的意思是「停止」,那麼也許你需要更多的邏輯來決定是否應該調用該方法。
我認爲他指的是元編程技術。 – Geo 2009-10-24 16:45:19
也許你想要NSObject
的-forwardInvocation
方法。這使您可以捕獲消息,重新定位它,然後重新發送。
該文檔指出* -forwardInvocation *僅在對象發送消息時沒有相應的方法時才調用。我希望在任何情況下都可以調用某個方法,即使接收方確實有該選擇器。 – luvieere 2009-10-24 17:01:17
這一評論比你原來的問題顯著的詳細信息...;) – MarkPowell 2009-10-24 17:25:39
我會編輯,感謝您指出。 – luvieere 2009-10-24 17:26:31
方法調用,no。發送消息,是的,但是如果你想得到一個好的答案,你將不得不更多地描述。
Obj-C中的方法調用的術語是「消息發送」,因此它們是一個,它們是相同的,而不是一個函數調用 - 你是正確的 - 不能被動態攔截。 – 2013-06-21 19:54:02
你可以用你自己的方法調用方法調用,它可以做任何你想做的「截取」操作,並調用原始實現。 Swizzling通過class_replaceMethod()完成。
發送在Objective-C消息被轉換成函數objc_msgSend(receiver, selector, arguments)
或其變體objc_msgSendSuper
,objc_msgSend_stret
,objc_msgSendSuper_stret
中的一個的呼叫。
如果可以更改這些函數的實現,我們可以攔截任何消息。不幸的是,objc_msgSend
是Objective-C運行時的一部分,不能被覆蓋。
通過Google搜索,我在Google圖書上找到一篇論文:A Reflective Architecture for Process Control Applications by Charlotte Pii Lunau。本文通過將對象的isa
類指針重定向到自定義MetaObject類的實例來介紹一種黑客攻擊。因此,用於修改對象的消息被髮送到MetaObject實例。由於MetaObject類沒有自己的方法,因此它可以通過將消息轉發給修改後的對象來響應前向調用。
該論文不包含源代碼的有趣部分,我不知道這種方法是否會在可可中產生副作用。但嘗試可能很有趣。
創建的NSProxy
一個子類,並實現-forwardInvocation:
和-methodSignatureForSelector:
(或-forwardingTargetForSelector:
,如果你只是指揮它到第二個對象,而不是用自己的方法擺弄)。
NSProxy
是設計用於實現-forwardInvocation:
類。它有幾種方法,但大多數你不希望它們被捕獲。例如,捕獲引用計數方法將阻止代理被解除分配,除非在垃圾回收下。但是,如果有上NSProxy
,你絕對需要提出具體方法,可以專門重寫方法和手動調用-forwardInvocation:
。這樣做僅僅是因爲一個方法是NSProxy
文檔中列出並不意味着NSProxy
實現它。注意,僅僅是預期,所有的代理對象有它。
如果這不會爲你工作,提供有關情況的其他詳細信息。
如果你想記錄的消息從應用程序代碼發送的-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;
和消息發送,你將有一個日誌。請注意,截取的對象內發送的發送不會被攔截,因爲它們將使用原始對象的「自我」指針發送。
如果你只是想記錄從外部調用的消息(通常代表呼籲,看看哪些類型的消息的時候,等),可以覆蓋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;
}
你可能是指@選擇器(發佈),而不是@選擇器(dealloc) – 2009-10-30 15:58:10
是的,我的意思是@選擇器(發佈)。這個我很可愛,並且從一些調試dealloc的代碼中粘貼出來,但不想在示例中展示,因爲這樣做有一些特殊問題。固定。 – 2009-10-30 18:43:49
@LouisGerbarg你可以發佈一個鏈接到您的原始來源,發佈swizzled? – funroll 2012-11-07 15:49:38