Reputation: 464
I'm doing my best to explain how this works, but it's pretty confusing and lengthy. Let me know if there's something I can do to clarify.
I am stucked in KVC and KVO concepts.
I have a Entry Class (NSObject).
Entry Class has private variable objectProperties (NSDictionary) who's value comes from the server.
Suppose objectProperties has key "price", "discount", etc then I want to create dynamic properties in Entry class's instance.
These key may vary according to the response and so as the dynamic variables.
Now when user want to use KVO on the same properties they are not accessible.
For creating dynamic properties and it's Getter/Setter:
-(NSString*)propName:(NSString*)name {
name = [name stringByReplacingOccurrencesOfString:@":" withString:@""];
NSRange r;
r.length = name.length -1 ;
r.location = 1;
NSString* firstChar = [name stringByReplacingCharactersInRange:r withString:@""];
if([firstChar isEqualToString:[firstChar lowercaseString]])
{return name;}
r.length = 1;
r.location = 0;
NSString* theRest = [name stringByReplacingCharactersInRange:r withString:@""];
return [NSString stringWithFormat:@"%@%@", [firstChar lowercaseString] , theRest];
}
-(NSString*)setterName:(NSString*)name {
name = [self propName:name];
NSRange r;
r.length = name.length -1 ;
r.location = 1;
NSString* firstChar = [name stringByReplacingCharactersInRange:r withString:@""];
r.length = 1;
r.location = 0;
NSString* theRest = [name stringByReplacingCharactersInRange:r withString:@""];
return [NSString stringWithFormat:@"set%@%@", [firstChar uppercaseString] , theRest];
}
-(NSString*)propNameFromSetterName:(NSString*)name {
NSRange r;
r.length = 3 ;
r.location = 0;
NSString* propName = [name stringByReplacingCharactersInRange:r withString:@""];
return [self propName:propName]; }
-(NSString*)ivarName:(NSString*)name {
NSRange r;
r.length = name.length -1 ;
r.location = 1;
NSString* firstChar = [name stringByReplacingCharactersInRange:r withString:@""].lowercaseString;
if([firstChar isEqualToString:@"_"])
return name;
r.length = 1;
r.location = 0;
NSString* theRest = [name stringByReplacingCharactersInRange:r withString:@""];
return [NSString stringWithFormat:@"_%@%@",firstChar, theRest]; }
NSObject *getter(id self, SEL _cmd)
{
NSString* name = NSStringFromSelector(_cmd);
NSString* ivarName = [self ivarName:name];
Ivar ivar = class_getInstanceVariable([self class], [ivarName UTF8String]);
return object_getIvar(self, ivar);
}
void setter(id self, SEL _cmd, NSObject *newObj)
{
NSString* name = [self propNameFromSetterName:NSStringFromSelector(_cmd)];
NSString* ivarName = [self ivarName:name];
Ivar ivar = class_getInstanceVariable([self class], [ivarName UTF8String]);
id oldObj = object_getIvar(self, ivar);
if (![oldObj isEqual: newObj])
{
object_setIvar(self, ivar, newObj);
[newObj copy];
}
}
-(NSDictionary *)createProperties:(NSArray *)propNames {
NSMutableDictionary* keys = [[NSMutableDictionary alloc]init];
for(NSString* key in propNames)
{
NSString* propName = [self propName: key];
NSString* iVarName = [self ivarName:propName];
class_addIvar([self class], [iVarName UTF8String] , sizeof(NSObject*), log2(sizeof(NSObject*)), @encode(NSObject));
objc_property_attribute_t a1 = { "T", "@\"NSObject\"" };
objc_property_attribute_t a2 = { "&", "" };
objc_property_attribute_t a3 = { "N", "" };
objc_property_attribute_t a4 = { "V", [iVarName UTF8String] };
objc_property_attribute_t attrs[] = { a1, a2, a3, a4};
class_addProperty([self class], [propName UTF8String], attrs, 4);
class_addMethod([self class], NSSelectorFromString(propName), (IMP)getter, "@@:");
class_addMethod([self class], NSSelectorFromString([self setterName:propName]), (IMP)setter, "v@:@");
id val = [self.objectProperties objectForKey:key];
[keys setValue:val forKey:propName];
}
return keys;
}
But when user want to observe any properties, it is not accessible in user class.
I am confused how to create dynamic properties and applying KVO.
I have also tried by making subclass of Entry class ie (myEntry which user creates at their end) and defining all variables there. but how do I set its value there? As I want that property to be readOnly.
Upvotes: 2
Views: 489
Reputation: 16660
First of all: You cannot create ivars after instantiating the class. This is, because the memory footprint of already created instances would change. So it is surprising that -createProperties:
is an instance method.
Additionally KVO works by subclassing the class of the observed instance at runtime and assigning the new class to the observed instance (isa-swizzling). This is obviously a problem, if you change the base class after registering for KVO.
However, all said applies to automatic KVO. You can use manual KVO instead. Simply add KVO notifications to your accessors.
But as commented: You should rethink your whole concept.
Upvotes: 1