2009-10-24 4 views
18

Можно ли перехватить вызов метода в Objective-C? Как?вызов метода перехвата в Objective-C

Edit: ответ Марк Пауэлл «s дал мне частичное решение, то -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]:"); 

Вы можете получить более подробную информацию в Objective C выполнения documentation.

+0

Вы, вероятно, имели в виду @selector (release), а не @selector (dealloc) –

+0

Да, я имел в виду @selector (release). Это я мило и вставляю это из некоторого кода, который swizzled dealloc, но не хотел показывать это в примере, потому что есть некоторые особые проблемы с этим. Исправлена. –

+0

@LouisGerbarg Можете ли вы разместить ссылку на исходный источник, который swizzled релиз? – funroll

-1

Чтобы что-то сделать при вызове метода, вы можете попробовать подход, основанный на событиях. Поэтому, когда метод вызывается, он транслирует событие, которое воспринимается всеми слушателями. Я не очень хорош с объективом C, но я просто понял что-то подобное, используя NSNotificationCenter в Cocoa.

Но если «перехватить» вы имеете в виду «остановить», то, возможно, вам нужно больше логики, чтобы решить, что метод должен быть вызван вообще.

+2

Я думаю, что он имеет в виду методы метапрограммирования. – Geo

1

Возможно, вы хотите NSObject-forwardInvocation. Это позволяет вам захватить сообщение, переназначить его и затем повторно отправить.

+2

В документации указано, что * -forwardInvocation * вызывается только тогда, когда объект отправляется сообщение, для которого у него нет соответствующего метода. Я бы хотел, чтобы метод вызывался при любых обстоятельствах, даже если у получателя есть этот селектор. – luvieere

+1

Этот комментарий имеет значительно больше информации, чем ваш оригинальный вопрос ...;) – MarkPowell

+0

Я отредактирую, спасибо за указание. – luvieere

0

Метод вызова, нет. Сообщение отправлено, да, но вам придется быть более подробным, если вы хотите получить хороший ответ о том, как это сделать.

+2

Термин для вызова метода в Obj-C является «отправкой сообщения», поэтому он один, и они одинаковы, в отличие от вызова функции, который - вы правы - не может быть динамически перехвачен. –

1

Вы можете совершить вызов метода одним из своих, что делает все, что вы хотите сделать на «перехвате», и обращается к первоначальной реализации. Swizzling выполняется с помощью class_replaceMethod().

15

Метод перехвата вызовов в Objective-C (при условии, что это Objective-C, а не C-вызов) выполняется с помощью метода, который называется метод swizzling методом .

Вы можете найти введение о том, как реализовать это here. Например, как метод swizzling реализован в реальном проекте, проверьте OCMock (Изолирующая среда для Objective-C).

11

Отправка сообщения в Objective-C преобразуется в вызов функции objc_msgSend(receiver, selector, arguments) или одного из его вариантов objc_msgSendSuper, objc_msgSend_stret, objc_msgSendSuper_stret.

Если бы было возможно изменить реализацию этих функций, мы могли бы перехватить любое сообщение. К сожалению, objc_msgSend является частью среды выполнения Objective-C и не может быть переопределен.

По googling Я нашел документ в 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: tip является частью решения.
Оберните объект:

@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; 

}