Recherche…


Remarques

Pour utiliser le runtime Objective-C, vous devez l'importer.

#import <objc/objc.h>

Joindre un objet à un autre objet existant (association)

Il est possible d'attacher un objet à un objet existant comme s'il y avait une nouvelle propriété. Cela s'appelle l' association et permet d'étendre des objets existants. Il peut être utilisé pour fournir du stockage lors de l'ajout d'une propriété via une extension de classe ou pour ajouter des informations supplémentaires à un objet existant.

L'objet associé est automatiquement libéré par le runtime une fois que l'objet cible est libéré.

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

Augmenter les méthodes en utilisant Method Swizzling

Le runtime Objective-C vous permet de modifier l'implémentation d'une méthode à l'exécution. Ceci est appelé méthode swizzling et est souvent utilisé pour échanger les implémentations de deux méthodes. Par exemple, si les méthodes foo et bar sont échangées, l'envoi du message foo va maintenant exécuter l'implémentation de bar et vice versa.

Cette technique peut être utilisée pour augmenter ou "patcher" les méthodes existantes que vous ne pouvez pas éditer directement, telles que les méthodes des classes fournies par le système.

Dans l'exemple suivant, la méthode -[NSUserDefaults synchronize] est augmentée pour imprimer l'heure d'exécution de l'implémentation d'origine.

IMPORTANT: beaucoup de gens essaient de faire des swizzling en utilisant method_exchangeImplementations . Cependant, cette approche est dangereuse si vous devez appeler la méthode que vous remplacez, car vous l'appellerez avec un sélecteur différent de celui attendu. Par conséquent, votre code peut se révéler étrange et inattendu, en particulier si plusieurs utilisateurs manipulent un objet de cette manière. Au lieu de cela, vous devriez toujours faire un swizzling en utilisant setImplementation conjointement avec une fonction C, vous permettant d'appeler la méthode avec le sélecteur d'origine.

#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

Si vous devez modifier une méthode qui prend des paramètres, vous devez simplement les ajouter en tant que paramètres supplémentaires à la fonction. Par exemple:

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

    ...
}

Méthodes d'appel directement

Si vous avez besoin d'appeler une méthode Objective-C à partir du code C, vous avez deux façons: d'utiliser objc_msgSend ou d'obtenir le pointeur de fonction d'implémentation de méthode ( IMP ) et de l'appeler.

#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 fonctionne en obtenant l'IMP pour la méthode et en l'appelant. Les IMP pour les dernières méthodes appelées sont mises en cache, donc si vous envoyez un message Objective-C dans une boucle très serrée, vous pouvez obtenir des performances acceptables. Dans certains cas, la mise en cache manuelle de l'IMP peut donner des performances légèrement meilleures, même s'il s'agit d'une optimisation de dernier recours.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow