Reputation: 51374
I have subclassed NSObject. And I need to set an object (NSMutableArray
instance) to an undefined key (@"ItemsList"
) to that class's instance. How can I do that? For exmaple, I want to set,
[myObject setValue:myArray forUndefinedKey:@"ItemsList"];
What are the things I should do to make this work?
Upvotes: 3
Views: 3375
Reputation: 498
@interface MyObject : NSObject
@end
@implementation MyObject {
NSMutableDictionary*itemsList;
}
- (id)valueForUndefinedKey:(NSString*)key {
if ([key isEqualToString:@"itemsList"]) {
return itemsList;
}
else {
return [super valueForUndefinedKey:key];
}
}
- (void)setValue:(id)value forUndefinedKey:(NSString*)key {
if ([key isEqualToString:@"itemsList"]) {
itemsList = value;
}
else {
[super setValue:value forUndefinedKey:key];
}
}
@end
And for calling attribute, it's necessary to call it via valueForKey:
MyObject*myObject = [MyObject new];
[myObject setValue:@[@"item1", @"item2"] forKey:@"itemsList"];
NSArray*itemsList = [myObject valueForKey:@"itemsList"];
Upvotes: 0
Reputation: 3236
You can use a class like this, and subclass it. ( see the subclass below, and notice how the testProperty is not synthesized, but is used as dynamic )
@interface INDynamicPersister : NSObject {
}
/*!
@method saveObject:forKey:
@abstract Invoked when a property setter is invoked.
*/
- (void) saveObject:(id)value forKey:(NSString *)key;
/*!
@method retrieveObjectForKey:
@abstract Invoked when a property getter needs its value.
*/
- (id) retrieveObjectForKey:(NSString *)key;
@end
#define Property_Setter_Suffix @":"
@implementation INDynamicPersister
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSMethodSignature *signature = [super methodSignatureForSelector:sel];
if ( signature == nil ) {
// get the name of the selector
NSString *selectorName = [NSStringFromSelector(sel) lowercaseString];
// if we have a setter property ...
if ( [selectorName hasSuffix:Property_Setter_Suffix] ) {
signature = [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
else {
signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];
}
}
return signature;
}
/*!
@method forwardInvocation:
@abstract This method will handle the invocation for the property getters and setters.
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// get the name of the selector
NSString *selectorName = [NSStringFromSelector([anInvocation selector]) lowercaseString];
// if we have a setter property ...
if ( [selectorName hasSuffix:Property_Setter_Suffix] ) {
int selectorLength = [selectorName length];
NSString *propertyName = [selectorName substringWithRange:NSMakeRange(3, selectorLength - 4)];
// get the value of the invocation
id invocationValue;
[anInvocation getArgument:&invocationValue atIndex:2];
// now set the value for the property
[self saveObject:invocationValue forKey:propertyName];
}
else {
// if we have a getter property ...
id invocationReturnValue = [self retrieveObjectForKey:selectorName];
[anInvocation setReturnValue:&invocationReturnValue];
}
}
- (void) saveObject:(id)value forKey:(NSString *)key {
[NSException raise:@"NotImplementedException" format:@"You need to override this method"];
}
- (id) retrieveObjectForKey:(NSString *)key {
[NSException raise:@"NotImplementedException" format:@"You need to override this method"];
return nil; // compiler is happy now
}
@end
The subclass could be something similar to this:
@interface MyDynamicEntity : INDynamicPersister {
NSMutableDictionary *propertyValues;
}
@property (retain, nonatomic) NSString * testProperty;
@end
@implementation MyDynamicEntity
@dynamic testProperty;
- (id) init {
if ( self = [super init] ) {
propertyValues = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void) dealloc {
[propertyValues release], propertyValues = nil;
[super dealloc];
}
- (void) saveObject:(id)value forKey:(NSString *)key {
if ( !value ) {
[propertyValues removeObjectForKey:key];
}
else {
[propertyValues setObject:value forKey:key];
}
}
- (id) retrieveObjectForKey:(NSString *)key {
return [propertyValues objectForKey:key];
}
@end
Hope this helps :)
Upvotes: 2
Reputation: 104698
here's one approach to easily accomplish this 'without having to create ivars':
@interface MONObject : NSObject
{
NSMutableDictionary * stuff;
}
// ....
- (void)setValue:(id)value forCustomKey:(NSString *)key;
@end
@implementation MONObject
- (void)setValue:(id)value forCustomKey:(NSString *)key
{
assert(self.stuff && value && key);
[self.stuff setValue:value forKey:key];
}
@end
Upvotes: 3