Поиск…


замечания

Чтобы использовать время выполнения Objective-C, вам необходимо импортировать его.

#import <objc/objc.h>

Прикрепить объект к другому существующему объекту (ассоциации)

Можно присоединить объект к существующему объекту, как если бы появилось новое свойство. Это называется ассоциацией и позволяет расширять существующие объекты. Он может использоваться для хранения данных при добавлении свойства через расширение класса или иным образом добавлять дополнительную информацию к существующему объекту.

Связанный объект автоматически освобождается средой выполнения после того, как целевой объект будет освобожден.

#import <objc/runtime.h>

// "Key" for association. Its value is never used and doesn't
// matter. The only purpose of this global static variable is to
// provide a guaranteed unique value at runtime: no two distinct 
// global variables can share the same address.
static char key;

id target = ...;
id payload = ...;
objc_setAssociateObject(target, &key, payload, OBJC_ASSOCIATION_RETAIN);
// Other useful values are OBJC_ASSOCIATION_COPY
// and OBJ_ASSOCIATION_ASSIGN

id queryPayload = objc_getAssociatedObject(target, &key);

Дополнительные методы с использованием метода Swizzling

Среда выполнения Objective-C позволяет вам изменить реализацию метода во время выполнения. Это называется методом swizzling и часто используется для обмена реализациями двух методов. Например, если методы foo и bar обмениваются, отправка сообщения foo теперь будет выполнять реализацию bar и наоборот.

Этот метод можно использовать для увеличения или «исправления» существующих методов, которые вы не можете редактировать напрямую, например, методов системных классов.

В следующем примере метод -[NSUserDefaults synchronize] для печати времени выполнения исходной реализации.

ВАЖНО: Многие люди пытаются делать swizzling, используя method_exchangeImplementations . Однако этот подход опасен, если вам нужно вызвать метод, который вы заменяете, потому что вы будете называть его с помощью другого селектора, который он ожидает получить. В результате ваш код может нарушаться странными и неожиданными способами, особенно если несколько участников подталкивают объект таким образом. Вместо этого вы всегда должны выполнять swizzling с помощью setImplementation в сочетании с функцией C, позволяя вам вызвать метод с помощью исходного селектора.

#import "NSUserDefaults+Timing.h"
#import <objc/runtime.h> // Needed for method swizzling

static IMP old_synchronize = NULL;

static void new_synchronize(id self, SEL _cmd);

@implementation NSUserDefaults(Timing)

+ (void)load
{
    Method originalMethod = class_getInstanceMethod([self class], @selector(synchronize:));
    IMP swizzleImp = (IMP)new_synchronize;
    old_synchronize = method_setImplementation(originalMethod, swizzleImp);
}
@end

static void new_synchronize(id self, SEL _cmd);
{
    NSDate *started;
    BOOL returnValue;

    started = [NSDate date];

    // Call the original implementation, passing the same parameters
    // that this function was called with, including the selector.
    returnValue = old_synchronize(self, _cmd);


    NSLog(@"Writing user defaults took %f seconds.", [[NSDate date] timeIntervalSinceDate:started]);

    return returnValue;
}

@end

Если вам нужно swizzle метод, который принимает параметры, вы просто добавляете их в качестве дополнительных параметров для функции. Например:

static IMP old_viewWillAppear_animated = NULL;
static void new_viewWillAppear_animated(id self, SEL _cmd, BOOL animated);

...

Method originalMethod = class_getClassMethod([UIViewController class], @selector(viewWillAppear:));
IMP swizzleImp = (IMP)new_viewWillAppear_animated;
old_viewWillAppear_animated = method_setImplementation(originalMethod, swizzleImp);

...

static void new_viewWillAppear_animated(id self, SEL _cmd, BOOL animated)
{
    ...

    old_viewWillAppear_animated(self, _cmd, animated);

    ...
}

Прямые вызовы

Если вам нужно вызвать метод Objective-C из кода C, у вас есть два пути: использование objc_msgSend или получение IMP (указателя функции реализации метода) и вызов этого.

#import <objc/objc.h>

@implementation Example

- (double)negate:(double)value {
    return -value;
}

- (double)invert:(double)value {
    return 1 / value;
}

@end

// Calls the selector on the object. Expects the method to have one double argument and return a double.
double performSelectorWithMsgSend(id object, SEL selector, double value) {
    // We declare pointer to function and cast `objc_msgSend` to expected signature.
    // WARNING: This step is important! Otherwise you may get unexpected results!
    double (*msgSend)(id, SEL, double) = (typeof(msgSend)) &objc_msgSend;

    // The implicit arguments of self and _cmd need to be passed in addition to any explicit arguments.
    return msgSend(object, selector, value);
}

// Does the same as the above function, but by obtaining the method's IMP.
double performSelectorWithIMP(id object, SEL selector, double value) {
    // Get the method's implementation.
    IMP imp = class_getMethodImplementation([self class], selector);

    // Cast it so the types are known and ARC can work correctly.
    double (*callableImp)(id, SEL, double) = (typeof(callableImp)) imp;

    // Again, you need the explicit arguments.
    return callableImp(object, selector, value);
} 

int main() {
    Example *e = [Example new];

    // Invoke negation, result is -4
    double x = performSelectorWithMsgSend(e, @selector(negate:), 4);

    // Invoke inversion, result is 0.25
    double y = performSelectorWithIMP(e, @selector(invert:), 4);
}

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow