Est-il possible d'ajouter des propriétés à un objet Objective C au moment de l'exécution?
Réponses
Trop de publicités?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.
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).
@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? .