Posted by: bearc0025 | June 8, 2012

iOS Dynamic Method Resolution

Apple docs on Dynamic Method Resolution

UPDATED FOR ARC BELOW.

If you want a class to respond to a given method w/o knowing what that method is when coding, you can use Dynamic Method Resolution.

In my case, I wanted to store some values in a dictionary not knowing what would be stored there (JSON parsed into a dictionary). Then have my class respond to getters/setters by looking for the given item as a key in the dictionary.

The answer here by Amy Worrall provides the bulk of the solution.

Basically you override resolveInstanceMethod: to call class_addMethod which adds accessorSetter or accessorGetter depending on the method being attempted.

+ (BOOL) resolveInstanceMethod:(SEL)aSEL
{
    NSString *method = NSStringFromSelector(aSEL);
    
    if ([method hasPrefix:@"set"])
    {
        class_addMethod([self class], aSEL, (IMP) accessorSetter, "v@:@");
        return YES;
    }
    else
    {
        class_addMethod([self class], aSEL, (IMP) accessorGetter, "@@:");
        return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}

Then implement those added methods, accessorSetter or accessorGetter, (I changed NSString to NSObject to allow for various class types to be stored in my dictionary. Also, for the setter, I remove ‘set’ and lowercase the next letter to turn something like setYikityBlar to yikityBlar)…

NSObject* accessorGetter(id self, SEL _cmd)
{
    NSString *method = NSStringFromSelector(_cmd);
    // Return the value of whatever key based on the method name
    return [((EventItem *)self)->valDict objectForKey:method];
}

void accessorSetter(id self, SEL _cmd, NSObject* newValue)
{
    NSString *method = NSStringFromSelector(_cmd);
    
    // remove set
    NSString *anID = [[method stringByReplacingCharactersInRange:NSMakeRange(0, 3) withString:@""] stringByReplacingOccurrencesOfString:@":" withString:@""];
    NSString *firstLowerChar = [[anID substringToIndex:1] lowercaseString];
    anID = [anID stringByReplacingCharactersInRange:NSMakeRange(0,1) withString:firstLowerChar];
    
    // Set value of the key anID to newValue
    [((EventItem *)self)->valDict setValue:newValue forKey:anID];
}

UPDATE FOR ARC:
ARC requires a bit more precision in the interface declaration – otherwise you get compile errors. There’s a couple things you should be able to do. If you actually have the item you want to dynamically create the getter/setters for, you can declare the methods to be dynamic (see link at the top of this post).

However, in my case, I don’t have them as properties (I set them in another class like a dictionary). So I declared the getters/setters in the header, but don’t implement them. I do get a compiler warning that my class’ implementation is incomplete. If anyone has a better solution, let me know.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: