Вызов метода перехвата в Objective-C - PullRequest
18 голосов
/ 24 октября 2009

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

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

Ответы [ 10 ]

23 голосов
/ 29 октября 2009

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

15 голосов
/ 24 октября 2009

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

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

11 голосов
/ 27 октября 2009

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

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

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

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

5 голосов
/ 20 августа 2011

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

3 голосов
/ 19 января 2012

Если вы хотите регистрировать только сообщения, вызываемые извне (обычно вызываемые делегатами; чтобы увидеть, какие сообщения, когда и т. Д.), Вы можете переопределить 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;

}

2 голосов
/ 30 октября 2009

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

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

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

1 голос
/ 24 октября 2009

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

1 голос
/ 24 октября 2009

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

0 голосов
/ 24 октября 2009

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

0 голосов
/ 24 октября 2009

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

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

...