Вызов метода перехвата в Objective-C

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

Изменить: ответМарк Пауэлл дал мне частичное решение, метод -forwardInvocation. Но в документации говорится, что -forwardInvocation вызывается только тогда, когда объекту отправляется сообщение, для которого у него нет соответствующего метода. Мне бы хотелось, чтобы метод вызывался при любых обстоятельствах, даже если у получателя есть этот селектор.

10 ответов

Решение

Вы делаете это путем быстрого вызова метода. Предполагая, что вы хотите получить все версии 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, "v@:");

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

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

Вы можете найти введение о том, как реализовать это здесь. Для примера того, как метод swizzling реализован в реальном проекте, ознакомьтесь с OCMock (Isolation Framework для Objective-C).

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

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

Посмотрев в Google, я нашла статью Шарлотты Пии Лунау " Google Книги: отражающая архитектура для приложений управления процессами". В статье вводится взлом путем перенаправления объекта isa указатель класса на экземпляр пользовательского класса MetaObject. Таким образом, сообщения, предназначенные для измененного объекта, отправляются в экземпляр MetaObject. Так как класс MetaObject не имеет собственных методов, он может затем ответить на прямой вызов путем пересылки сообщения модифицированному объекту.

Статья не содержит интересных фрагментов исходного кода, и я не знаю, будет ли такой подход иметь побочные эффекты в Какао. Но это может быть интересно попробовать.

Если вы хотите регистрировать сообщения, отправленные из кода вашего приложения, то -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;

и у вас будет журнал отправки сообщений. Обратите внимание, что посылки, отправленные из перехваченного объекта, не будут перехвачены, поскольку они будут отправлены с использованием исходного указателя "self" объекта.

Если вы хотите регистрировать только сообщения, вызываемые извне (обычно вызываемые делегатами; чтобы увидеть, какие сообщения, когда и т. Д.), Вы можете переопределить responsedsToSelector следующим образом:

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

}

Создать подкласс NSProxy и реализовать -forwardInvocation: а также -methodSignatureForSelector: (или же -forwardingTargetForSelector:, если вы просто направляете его на второй объект, а не возитесь с методом самостоятельно).

NSProxy это класс, предназначенный для реализации -forwardInvocation: на. У него есть несколько методов, но в основном вы не хотите, чтобы их поймали. Например, перехват методов подсчета ссылок предотвратит освобождение прокси, кроме как при сборке мусора. Но если есть конкретные методы NSProxy что вам абсолютно необходимо переслать, вы можете переопределить этот метод специально и вызвать -forwardInvocation: вручную. Обратите внимание, что просто потому, что метод указан под NSProxy документация не означает, что NSProxy реализует его, просто ожидается, что он есть у всех прокси-объектов.

Если это не сработает, предоставьте дополнительную информацию о вашей ситуации.

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

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

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

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

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

Другие вопросы по тегам