Suche…


Bemerkungen

Um die Objective-C-Laufzeitumgebung verwenden zu können, müssen Sie sie importieren.

#import <objc/objc.h>

Objekt an ein anderes vorhandenes Objekt anhängen (Assoziation)

Es ist möglich, ein Objekt an ein vorhandenes Objekt anzuhängen, als ob eine neue Eigenschaft vorhanden wäre. Dies wird Assoziation genannt und ermöglicht das Erweitern vorhandener Objekte. Es kann verwendet werden, um Speicher bereitzustellen, wenn eine Eigenschaft über eine Klassenerweiterung hinzugefügt wird, oder um einem vorhandenen Objekt zusätzliche Informationen hinzuzufügen.

Das zugehörige Objekt wird automatisch von der Laufzeit freigegeben, sobald das Zielobjekt freigegeben wird.

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

Methoden mit der Methode Swizzling erweitern

Mit der Objective-C-Laufzeit können Sie die Implementierung einer Methode zur Laufzeit ändern. Diese Methode wird als Swizzling-Methode bezeichnet und wird häufig zum Austausch der Implementierungen zweier Methoden verwendet. Wenn zum Beispiel die Methoden foo und bar ausgetauscht werden, führt das Senden der Nachricht foo nun die Implementierung von bar und umgekehrt.

Diese Technik kann verwendet werden, um vorhandene Methoden zu erweitern oder zu "patchen", die Sie nicht direkt bearbeiten können, wie z. B. Methoden von vom System bereitgestellten Klassen.

Im folgenden Beispiel wird die Methode -[NSUserDefaults synchronize] erweitert, um die Ausführungszeit der ursprünglichen Implementierung zu drucken.

WICHTIG: Viele Leute versuchen, mithilfe von method_exchangeImplementations Swizzling method_exchangeImplementations . Dieser Ansatz ist jedoch gefährlich, wenn Sie die zu ersetzende Methode aufrufen müssen, da Sie sie mit einem anderen Selektor aufrufen, als er erwartet. Dies führt dazu, dass Ihr Code auf seltsame und unerwartete Weise beschädigt werden kann - insbesondere, wenn mehrere Parteien ein Objekt auf diese Weise schwenken. Stattdessen sollten Sie immer mit setImplementation in Verbindung mit einer C-Funktion einen Swizzling setImplementation , sodass Sie die Methode mit dem ursprünglichen Selektor aufrufen können.

#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

Wenn Sie eine Methode, die Parameter verwendet, umschalten müssen, fügen Sie sie einfach als zusätzliche Parameter zur Funktion hinzu. Zum Beispiel:

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

    ...
}

Methoden direkt aufrufen

Wenn Sie eine Objective-C-Methode aus C-Code aufrufen müssen, haben Sie zwei Möglichkeiten: Verwenden Sie objc_msgSend oder objc_msgSend den IMP (Method Implementation Function Pointer) ab und rufen Sie ihn auf.

#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 funktioniert, indem der IMP für die Methode objc_msgSend wird. Die IMP der letzten verschiedenen aufgerufenen Methoden werden zwischengespeichert. Wenn Sie also eine Objective-C-Nachricht in einer sehr engen Schleife senden, können Sie eine akzeptable Leistung erzielen. In einigen Fällen kann das manuelle Zwischenspeichern des IMP zu einer etwas besseren Leistung führen, obwohl dies eine Optimierung des letzten Auswegs ist.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow