수색…


비고

Objective-C 런타임을 사용하려면 가져와야합니다.

#import <objc/objc.h>

개체를 다른 기존 개체에 연결 (연결)

새 속성이있는 것처럼 기존 개체에 개체를 첨부 할 수 있습니다. 이를 연관 (association )이라고하며, 기존 객체를 확장 할 수 있습니다. 클래스 확장을 통해 속성을 추가 할 때 저장소를 제공하거나 기존 개체에 추가 정보를 추가하는 데 사용할 수 있습니다.

연관된 객체는 대상 객체가 할당 해제되면 런타임에 의해 자동으로 해제됩니다.

#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 )이라고하며, 종종 두 가지 메소드의 구현을 교환하는 데 사용됩니다. 예를 들어, 메소드 foobar 가 교환되면, foo 라는 메세지를 보내면 bar 의 구현이 실행되고, 그 반대의 경우도 마찬가지입니다.

이 기술은 시스템 제공 클래스의 메소드와 같이 직접 편집 할 수없는 기존 메소드를 보강하거나 "패치"하는 데 사용할 수 있습니다.

다음 예제에서는 -[NSUserDefaults synchronize] 메소드가 원래 구현의 실행 시간을 인쇄하기 위해 추가됩니다.

중요 : 많은 사람들이 method_exchangeImplementations 사용하여 method_exchangeImplementations 하려고합니다. 그러나이 방법은 대체하려는 메서드를 호출해야하는 경우 위험합니다. 왜냐하면 사용자가받을 것으로 예상되는 다른 선택기를 사용하여 호출하기 때문입니다. 결과적으로 코드가 이상하고 예기치 않은 방식으로 중단 될 수 있습니다. 특히 여러 사람이이 방법으로 객체를 선택하는 경우 더욱 그렇습니다. 대신 C 함수와 함께 setImplementation 을 사용하여 언제나 setImplementation 을 수행해야합니다. 그러면 원래 선택기로 메서드를 호출 할 수 있습니다.

#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);

    ...
}

메소드 직접 호출

C 코드에서 Objective-C 메서드를 호출해야하는 경우 objc_msgSend 를 사용하거나 IMP (메서드 구현 함수 포인터)를 objc_msgSend 호출하는 두 가지 방법이 있습니다.

#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를 objc_msgSend 호출함으로써 작동합니다. 호출 된 마지막 몇 가지 메서드에 대한 IMP 는 캐시되므로 Objective-C 메시지를 매우 엄격한 루프에서 보내는 경우 허용되는 성능을 얻을 수 있습니다. 경우에 따라 IMP를 수동으로 캐싱하면 최후의 최적화가되지만 약간 더 나은 성능을 제공 할 수 있습니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow