Ricerca…


Osservazioni

Per utilizzare il runtime Objective-C, è necessario importarlo.

#import <objc/objc.h>

Allegare oggetto ad un altro oggetto esistente (associazione)

È possibile associare un oggetto a un oggetto esistente come se ci fosse una nuova proprietà. Questa è chiamata associazione e consente di estendere oggetti esistenti. Può essere utilizzato per fornire spazio di archiviazione quando si aggiunge una proprietà tramite un'estensione di classe o altrimenti si aggiungono informazioni aggiuntive a un oggetto esistente.

L'oggetto associato viene rilasciato automaticamente dal runtime una volta che l'oggetto target è stato deallocato.

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

Metodi di incremento usando il Metodo Swizzling

Il runtime Objective-C consente di modificare l'implementazione di un metodo in fase di esecuzione. Questo è chiamato metodo swizzling ed è spesso usato per scambiare le implementazioni di due metodi. Ad esempio, se i metodi foo e bar sono scambiati, l'invio del messaggio foo ora eseguirà l'implementazione della bar e viceversa.

Questa tecnica può essere utilizzata per aumentare o "patchare" i metodi esistenti che non è possibile modificare direttamente, come i metodi delle classi fornite dal sistema.

Nell'esempio seguente, il metodo -[NSUserDefaults synchronize] è aumentato per stampare il tempo di esecuzione dell'implementazione originale.

IMPORTANTE: molte persone provano a fare swizzling usando method_exchangeImplementations . Tuttavia, questo approccio è pericoloso se devi chiamare il metodo che stai sostituendo, perché lo chiamerai utilizzando un selettore diverso da quello che si aspetta di ricevere. Di conseguenza, il tuo codice può spezzare in modi strani e inaspettati, in particolare se più persone ingoiano un oggetto in questo modo. Invece, dovresti sempre fare lo swizzling usando setImplementation in combinazione con una funzione C, permettendoti di chiamare il metodo con il selettore originale.

#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

Se hai bisogno di swizzle un metodo che prende parametri, devi solo aggiungerli come parametri aggiuntivi alla funzione. Per esempio:

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

    ...
}

Chiamare i metodi direttamente

Se hai bisogno di chiamare un metodo Objective-C dal codice C, hai due modi: usare objc_msgSend , o ottenere l' IMP (puntatore della funzione di implementazione del metodo) e chiamarlo.

#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 funziona ottenendo l'IMP per il metodo e chiamandolo. Gli IMP degli ultimi metodi chiamati sono memorizzati nella cache, quindi se stai inviando un messaggio Objective-C in un ciclo molto stretto puoi ottenere prestazioni accettabili. In alcuni casi, il caching manuale dell'IMP può fornire prestazioni leggermente migliori, sebbene si tratti di un'ottica di ultima istanza.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow