NSInvocation Tip

NSInvocation certainly gets a bad rap at times. If you use it naively, you will get some horrible performance with it. Doubly so on the iPhone where there is much less CPU power than on MacOS X. But for some things, it is very useful. For example, say you want to pass a field name to a method and then call the setter and getter for that field on an object. Using a selector and an NSInvocation object, you can dynamically call them:

-(float)invokeGetSelectorForField:(NSString *)fieldName inObject:(id)o {
    float value;
    SEL fieldGetSelector = NSSelectorFromString(fieldName);
    NSInvocation *i = [NSInvocation invocationWithMethodSignature:
        [[o class]
        instanceMethodSignatureForSelector:fieldGetSelector]];
    [i setSelector:fieldGetSelector];
    [i setTarget:o];
    [i invoke];
    [i getReturnValue:&value];
    return value;
}

But what if you need to call this getter multiple times? Maybe hundreds or thousands of times? In this case, you can keep your NSInvocation object around and then simply invoke and then call getReturnValue: on it anytime you need to make the call. You can change the target of the NSInvocation to point to a completely different object. Or even change the selector. Pretty much anything you want to change you can. So instead of the above code, do something like this instead:

In your init method:

    SEL fieldGetSelector = NSSelectorFromString(fieldName);
    fieldGetInvocation = [[NSInvocation invocationWithMethodSignature:
        [[o class]
        instanceMethodSignatureForSelector:fieldGetSelector]] retain];
    [fieldGetInvocation setSelector:fieldGetSelector];
    [fieldGetInvocation setTarget:o];

In your dealloc method:

    [fieldGetInvocation release];

And then when you need to call it:

    float value;
    [fieldGetInvocation invoke];
    [fieldGetInvocation getReturnValue:&value];

Doing this can easily end up saving a lot of CPU usage in your application, depending on how often you are calling the method. In my current project, I went from 20% usage in calling the setter and getters down to effectively 0% when measured with Instruments. All of that time was being eaten up in creating and destroying NSInvocation objects. Certainly not the best usage of the limited resources on the iPhone.

For more information on the NSInvocation class, please take a look at the documentation on Apple’s site: http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/nsinvocation_Class/Reference/Reference.html

Advertisements

About this entry