41 votes

Comment puis-je ajouter des propriétés à un objet au moment de l'exécution?

Est-il possible d'ajouter des propriétés à un objet Objective C au moment de l'exécution?

62voto

Bavarious Points 43993

Il est possible d'ajouter des propriétés formelles d'une classe via class_addProperty():

BOOL class_addProperty(Class cls,
    const char *name,
    const objc_property_attribute_t *attributes,
    unsigned int attributeCount)

Les deux premiers paramètres sont auto-explicatif. Le troisième paramètre est un tableau de la propriété des attributs, et chaque propriété de l'attribut est une paire nom-valeur qui suivent Objective-C type de codages pour déclarées propriétés. Notez que la documentation mentionne encore la chaîne séparée par des virgules pour le codage des attributs de la propriété. Chaque segment de la chaîne séparée par des virgules est représenté par un objc_property_attribute_t de l'instance. En outre, objc_property_attribute_t accepte les noms de classe d'ailleurs le générique de l' @ type de codage de l' id.

Voici une première ébauche d'un programme dynamique qui ajoute une propriété appelée name d'une classe qui possède déjà une variable d'instance appelée _privateName:

#include <objc/runtime.h>
#import <Foundation/Foundation.h>

@interface SomeClass : NSObject {
    NSString *_privateName;
}
@end

@implementation SomeClass
- (id)init {
    self = [super init];
    if (self) _privateName = @"Steve";
    return self;
}
@end

NSString *nameGetter(id self, SEL _cmd) {
    Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
    return object_getIvar(self, ivar);
}

void nameSetter(id self, SEL _cmd, NSString *newName) {
    Ivar ivar = class_getInstanceVariable([SomeClass class], "_privateName");
    id oldName = object_getIvar(self, ivar);
    if (oldName != newName) object_setIvar(self, ivar, [newName copy]);
}

int main(void) {
    @autoreleasepool {
        objc_property_attribute_t type = { "T", "@\"NSString\"" };
        objc_property_attribute_t ownership = { "C", "" }; // C = copy
        objc_property_attribute_t backingivar  = { "V", "_privateName" };
        objc_property_attribute_t attrs[] = { type, ownership, backingivar };
        class_addProperty([SomeClass class], "name", attrs, 3);
        class_addMethod([SomeClass class], @selector(name), (IMP)nameGetter, "@@:");
        class_addMethod([SomeClass class], @selector(setName:), (IMP)nameSetter, "v@:@");

        id o = [SomeClass new];
        NSLog(@"%@", [o name]);
        [o setName:@"Jobs"];
        NSLog(@"%@", [o name]);
    }
}

Son (coupé) de sortie:

Steve
Jobs

Les getter et setter devrait plus être rédigé avec soin, mais cela devrait être suffisant comme un exemple de comment ajouter dynamiquement un régime de la propriété au moment de l'exécution.

9voto

Jack Points 61503

Si vous jetez un oeil à l' NSKeyValueCoding protocole, documenté ici, vous pouvez voir qu'il y a un message disant:

- (id)valueForUndefinedKey:(NSString *)key

Vous pouvez surcharger cette méthode pour fournir à votre résultat personnalisé pour la propriété non définie. Bien sûr, cela suppose que votre classe utilise le protocole correspondant.

Ce type d'approche est couramment utilise pour fournir des inconnus comportement de classes (par exemple. un sélecteur qui n'existe pas).

4voto

hypercrypt Points 8032

@properties - no (utilisation de la syntaxe à points, etc.). Mais vous pouvez ajouter du stockage en utilisant des objets associés: Comment utiliser objc_setAssociatedObject / objc_getAssociatedObject dans un objet? .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X