Zoeken…


Opmerkingen

Om de Objective-C runtime te gebruiken, moet u deze importeren.

#import <objc/objc.h>

Object koppelen aan een ander bestaand object (associatie)

Het is mogelijk om een object aan een bestaand object te koppelen alsof er een nieuwe eigenschap is. Dit wordt associatie genoemd en maakt het mogelijk bestaande objecten uit te breiden. Het kan worden gebruikt om opslag te bieden bij het toevoegen van een eigenschap via een klasse-extensie of om op een andere manier extra informatie aan een bestaand object toe te voegen.

Het bijbehorende object wordt automatisch vrijgegeven door de runtime zodra het doelobject is toegewezen.

#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 voor het gebruik van Methode Swizzling

Met de Objective-C runtime kunt u de implementatie van een methode tijdens runtime wijzigen. Dit wordt methode-swizzling genoemd en wordt vaak gebruikt om de implementaties van twee methoden uit te wisselen. Als bijvoorbeeld de methoden foo en bar worden uitgewisseld, zal het verzenden van het bericht foo nu de implementatie van bar en vice versa.

Deze techniek kan worden gebruikt om bestaande methoden die u niet rechtstreeks kunt bewerken, zoals methoden van door het systeem geleverde klassen, te vergroten of te "patchen".

In het volgende voorbeeld wordt de -[NSUserDefaults synchronize] -methode uitgebreid om de uitvoeringstijd van de oorspronkelijke implementatie af te drukken.

BELANGRIJK: Veel mensen proberen te swizzlen met method_exchangeImplementations . Deze benadering is echter gevaarlijk als u de methode die u vervangt moet aanroepen, omdat u deze aanroept met een andere selector dan verwacht wordt. Als gevolg hiervan kan uw code op vreemde en onverwachte manieren breken - vooral als meerdere partijen een object op deze manier overspoelen. In plaats daarvan moet u altijd swizzling doen met setImplementation in combinatie met een C-functie, zodat u de methode kunt oproepen met de originele selector.

#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

Als u een methode waarvoor parameters nodig zijn, moet swizzle, voegt u ze gewoon toe als extra parameters aan de functie. Bijvoorbeeld:

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

    ...
}

Oproepmethoden direct

Als u een Objective-C-methode uit C-code moet aanroepen, kunt u op twee manieren: objc_msgSend of de IMP (methode-implementatiefunctie) verkrijgen en die aanroepen.

#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 werkt door de IMP voor de methode te verkrijgen en die aan te roepen. De IMP 's voor de laatste verschillende methoden worden in de cache opgeslagen, dus als u een Objective-C-bericht in een zeer strakke lus verzendt, kunt u acceptabele prestaties krijgen. In sommige gevallen kan het handmatig cachen van het IMP iets betere prestaties geven, hoewel dit een laatste redmiddel is.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow