サーチ…


備考

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

メソッドスウィズリングを使用したメソッドの拡張

Objective-Cランタイムでは、実行時にメソッドの実装を変更できます。これはメソッドスウィージングと呼ばれ、2つのメソッドの実装を交換するためによく使用されます。たとえば、メソッドfoobarが交換された場合、メッセージfooを送信すると、 barの実装が実行され、その逆も実行されbar

このテクニックは、システムが提供するクラスのメソッドなど、直接編集できない既存のメソッドを拡張または「パッチする」ために使用できます。

次の例では、 -[NSUserDefaults synchronize]メソッドが拡張され、元の実装の実行時間を出力します。

重要:多くの人が、 method_exchangeImplementationsを使用してmethod_exchangeImplementationsmethod_exchangeImplementationsうとします。ただし、置換するメソッドを呼び出す必要がある場合は、受け取る予定のセレクターとは異なるセレクターを使用して呼び出すため、この方法は危険です。結果として、あなたのコードが奇妙で予期しない方法で壊れる可能性があります。代わりに、C関数と組み合わせて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

パラメータを取るメソッドを振る舞わせる必要がある場合は、それらを追加のパラメータとして関数に追加するだけです。例えば:

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がキャッシュされるため、Objective-Cメッセージを非常にタイトなループで送信しても、許容できるパフォーマンスを得ることができます。場合によっては、IMPを手動でキャッシュすると、最後の最適化の最適化ではあるが、わずかに優れたパフォーマンスが得られることがあります。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow