Sök…


Anmärkningar

För att kunna använda Objekt-C-runtime måste du importera den.

#import <objc/objc.h>

Bifoga objekt till ett annat existerande objekt (associering)

Det är möjligt att koppla ett objekt till ett befintligt objekt som om det fanns en ny egenskap. Detta kallas förening och gör att man kan utöka befintliga objekt. Det kan användas för att tillhandahålla lagring när du lägger till en egenskap via en klasstillägg eller på annat sätt lägger till ytterligare information till ett befintligt objekt.

Det tillhörande objektet släpps automatiskt av körtiden när målobjektet har omlokaliserats.

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

Förstärkningsmetoder med Method Swizzling

Objekt-C-runtime låter dig ändra implementeringen av en metod vid runtime. Detta kallas metodswizzling och används ofta för att utbyta implementeringarna av två metoder. Om till exempel metoderna foo och bar utbyts kommer nu att skicka meddelandet foo att genomföra implementeringen av bar och vice versa.

Den här tekniken kan användas för att förstärka eller "korrigera" befintliga metoder som du inte kan redigera direkt, till exempel metoder för systemtillhandahållna klasser.

I följande exempel -[NSUserDefaults synchronize] metoden -[NSUserDefaults synchronize] för att skriva ut exekveringstiden för den ursprungliga implementeringen.

VIKTIGT: Många människor försöker göra swizzling med method_exchangeImplementations . Men detta tillvägagångssätt är farligt om du behöver ringa den metod du ersätter, eftersom du kommer att ringa den med en annan väljare än den förväntar sig att få. Som ett resultat kan din kod bryta på konstiga och oväntade sätt - speciellt om flera parter snör ett objekt på detta sätt. Istället bör du alltid svänga med setImplementation tillsammans med en C-funktion, så att du kan ringa metoden med den ursprungliga väljaren.

#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

Om du behöver swizzla en metod som tar parametrar lägger du bara till dem som ytterligare parametrar till funktionen. Till exempel:

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

    ...
}

Ringmetoder direkt

Om du behöver ringa en Objekt-C-metod från C-kod, har du två sätt: använda objc_msgSend , eller få IMP (metodimplementeringsfunktionspekaren) och ringa det.

#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 fungerar genom att skaffa IMP för metoden och kalla det. IMP erna för de sista flera anropade metoderna cachas, så om du skickar ett Objekt-C-meddelande i en mycket snäv slinga kan du få acceptabla prestanda. I vissa fall kan manuell cache av IMP ge något bättre prestanda, även om detta är en sista utväg optimering.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow