Вопрос

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

Редактировать: Марк Пауэллэтот ответ дал мне частичное решение, -Переадресация способ.Но в документации указано, что -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, "v@:");

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

Другие советы

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

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

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

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

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

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

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

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

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

- (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 способ.Это позволяет вам перехватить сообщение, перенацелить его, а затем отправить повторно.

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

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

Чтобы что-то сделать при вызове метода, вы могли бы попробовать подход, основанный на событиях.Таким образом, когда вызывается метод, он транслирует событие, которое улавливается любыми слушателями.Я не силен в objective C, но я только что придумал нечто подобное, используя Центр идентификации NSNotification center в Какао.

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top