Est-il possible d'ajouter des propriétés à un objet Objective C lors de l'exécution?
Il est possible d'ajouter des propriétés formelles à 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 explicites. Le troisième paramètre est un tableau d'attributs de propriété, et chaque attribut de propriété est une paire nom-valeur qui suit Objective-C codages de type pour propriétés déclarées . Notez que la documentation mentionne toujours la chaîne séparée par des virgules pour le codage des attributs de propriété. Chaque segment de la chaîne séparée par des virgules est représenté par un objc_property_attribute_t
exemple. En outre, objc_property_attribute_t
accepte les noms de classe en plus du générique @
codage de type de id
.
Voici un premier brouillon d'un programme qui ajoute dynamiquement une propriété appelée name
à une classe qui a 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]);
}
}
Sa sortie (rognée):
Steve
Jobs
Les méthodes getter et setter doivent être écrites plus attentivement, mais cela devrait suffire comme exemple de la façon d'ajouter dynamiquement une propriété formelle à l'exécution.
Si vous regardez le protocole NSKeyValueCoding
, documenté ici , vous pouvez voir qu'il y a un message appelé:
- (id)valueForUndefinedKey:(NSString *)key
Vous devez remplacer cette méthode pour fournir votre résultat personnalisé pour la propriété non définie spécifiée. Bien sûr, cela suppose que votre classe utilise le protocole correspondant.
Ce type d'approche est couramment utilisé pour fournir un comportement inconnu aux classes (par exemple, un sélecteur qui n'existe pas).
@properties - non (c'est-à-dire en utilisant 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? .