<

NSInvocation Basics

Published on
18,022 Points
11,722 Views
3 Endorsements
Last Modified:
Approved
Hamidreza Vakilian
+14 years of experience in software development. We provide top quality apps, web and integrated solutions.
In this article I'm going to cover the basics and usages of NSInvocation.

What is NSInvocation?

Apple Developer Reference describes NSInvocation this way:
An NSInvocation is an Objective-C message rendered static, that is, it is an action turned into an object. NSInvocation objects are used to store and forward messages between objects and between applications, primarily by NSTimer objects and the distributed objects system.

When we call a method or access a member data of an object in our application, we are actually sending a message. Usually we don't send messages explicitly in our code since the compiler does the work for us. What NSInvocation brings to us is the ability to manually send a message to an object. So a NSInvocation object's job is to send messages; hence we create an object of NSInvocation class, we describe the aspects of the message and finally we send it to our target. The most useful advantage of NSInvocation is that it's an object so we can pass it as an argument to other methods.

Simple NSInvocation example

Suppose we have a dummy object named myDummyObject and it is an object of class myClass. in myClass we have a defined method named sayHelloWorld without any arguments. The simplest way to execute sayHelloWorld method is:
[myDummyObject sayHelloWorld];

Open in new window

In fact this line causes the compiler to create a message which says "execute sayHelloWorld method" and sends it to the target object which is myDummyObject here.
Now we want to create the same message manually and send it to myDummyObject using NSInvocation.

A selector describes which method we want to call:
SEL selector = @selector(sayHelloWorld);

Open in new window


Our NSInvocation object needs to know whether the target object has declared the method or if not throws an exception, so it should utilize some search algorithm to achieve this: A method signature is fingerprint of a method which depends on its return value, number of arguments it takes and their types. Therefore our NSInvocation will be able to search through the target object's methods list to see whether it has defined the method or not. This signature is created using methodSignatureForSelector method which every NSObject descendent has this method. In this case sayHelloWorld is a method of myClass. myDummyObject is an object of myClass so we should call methodSignatureForSelector on myDummyObject to return the signature for the method named sayHelloWorld using the selector we declared above:
NSMethodSignature *signature = [myDummyObject methodSignatureForSelector:selector];

Open in new window


We create our NSInvocation object based on the method signature we declared:
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

Open in new window


NSInvocation objects also wants to have our selector:
        [invocation setSelector:selector];

Open in new window


Here we specified our target object which we want our message to be sent to:
        [invocation setTarget:myDummyObject];

Open in new window


The final step here. Commanding our NSInvocation object to send the message.
        [invocation invoke];

Open in new window


NSInvocation to call a method with arguments

Suppose myDummyObject contains a method as follows:
-(void)myFunctionWithArg1: (NSString*) arg1 Arg2: (NSString *) arg2 Arg3: (NSString *) arg3 Arg4: (NSString *) arg4
{
    NSLog(@"I am a function with 4 arguments: <%@> <%@> <%@> <%@>" , arg1, arg2, arg3, arg4);
}

Open in new window


The normal way of calling this method is:
[myDummyObject myFunctionWithArg1:@"First" Arg2:@"Second" Arg3:@"Third" Arg4:@"Fourth"];

Open in new window


Now we want to create the message manually using NSInvocation. As I explained above:
SEL selector = @selector(myFunctionWithArg1:Arg2:Arg3:Arg4:);
    NSMethodSignature *signature = [myDummyObject methodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
    [invocation setTarget:myDummyObject];
    [invocation setSelector:selector];

Open in new window


We define four strings. We will pass them through our NSInvocation toward the target object:
    NSString *arg1 = @"First";
    NSString *arg2 = @"Second";
    NSString *arg3 = @"Third";
    NSString *arg4 = @"Fourth";

Open in new window


This is the way to set the arguments for the method. You can use SetArgument:atIndex for each argument at the specified index.
Some notes about SetArgument:atIndex method:
1- The first argument should be the address of the variable you want to set. (You may encounter bridging if you are on ARC mode)
2- Indexes start at 2 so e.g. index 2 is the first argument of the method.
    [invocation setArgument:&arg1 atIndex:2];
    [invocation setArgument:&arg2 atIndex:3];
    [invocation setArgument:&arg3 atIndex:4];
    [invocation setArgument:&arg4 atIndex:5];

Open in new window


And finally invoking our message:
    [invocation invoke];

Open in new window


Using NSInvocation for call-backs

The most interesting usage of NSInvocation is that we can setup a call-back method for another method using NSInvocation. Now you may ask "what is a call-back method?" There are many asynchronous operations such as NSURLConnection (which is bound to transfer some data through a network) or NSXMLParser (which has the duty to parse a XML document). Most times these operations do not return their results immediately. We can request and setup many of such operations to call our desired methods (call-back method) when they are done (may also pass the result to our call-back method through arguments).

NSInvocation with call-back example

Suppose myDummyObject has a method named processMyTasksWithCallBackFunction as follows: processMyTasksWithCallBackFunction dispatches its tasks asynchronously using GCD.
-(void)processMyTasksWithCallBackFunction: (NSInvocation *) invocation
{
    dispatch_async(dispatch_get_main_queue(), ^{

        @autoreleasepool {
            NSLog(@"Performing tasks like accessing a web service or preparing data for app");
            sleep(3);//waiting 3 seconds
            NSLog(@"DONE! Now calling the callback function...");
            
            ///////// tasks are done; return the results through the NSInvocation object
            ///////// NSInvocation is supposed to point to a method which takes 4 NSString arguments
            
            NSString *arg1 = @"Result1";
            NSString *arg2 = @"Result2";
            NSString *arg3 = @"Result3";
            NSString *arg4 = @"Result4";
            [invocation setArgument:&arg1 atIndex:2];
            [invocation setArgument:&arg2 atIndex:3];
            [invocation setArgument:&arg3 atIndex:4];
            [invocation setArgument:&arg4 atIndex:5];
            [invocation invoke];   
        }  
    });
}

Open in new window


Now we want to call processMyTasksWithCallBackFunction method and grab the results. Before that we have to declare a call-back method which takes 4 arguments (in our example). We declare this method in current class where we are calling processMyTasksWithCallBackFunction from. (Of course we can setup the call-back method from a remote object too.)
-(void)myFunctionWithArg1: (NSString*) arg1 Arg2: (NSString *) arg2 Arg3: (NSString *) arg3 Arg4: (NSString *) arg4
{
    NSLog(@"I am a call-back method with 4 arguments: <%@> <%@> <%@> <%@>" , arg1, arg2, arg3, arg4);
}

Open in new window


Now we have to create the NSInvocation for our call-back method to be passed to processMyTasksWithCallBackFunction. Notice we don't set the arguments here because processMyTasksWithCallBackFunction will set them when it's done.
    SEL CB_selector = @selector(myFunctionWithArg1:Arg2:Arg3:Arg4:);
    NSMethodSignature *CB_signature = [self methodSignatureForSelector:CB_selector];
    NSInvocation *CB_invocation = [NSInvocation invocationWithMethodSignature:CB_signature];
    [CB_invocation setTarget:self];//Since we declared the call-back method in current class
    [CB_invocation setSelector:CB_selector];

Open in new window


Now we can call the asynchronous method:
[processMyTasksWithCallBackFunction: CB_invocation];

Open in new window


Sample Output:
2011-09-13 12:35:35.601 InvocationTest[6710:4903] Performing tasks like accessing a web service or preparing data for app
2011-09-13 12:35:38.603 InvocationTest[6710:4903] DONE! Now calling the callback function...
2011-09-13 12:35:38.604 InvocationTest[6710:4903] I am a call-back method with 4 arguments: <Result1> <Result2> <Result3> <Result4>


Conclusion

NSInvocation can bring great flexibility to your applications/frameworks in many situations. I hope this clarified NSInvocation and its usages. Please let me know if you have any question.
3
Comment
2 Comments
 

Administrative Comment

by:Eric AKA Netminder
Programmer-x,

Congratulations; your article has been published. Thank you for your submission; I'm not a  programmer, and your article was clear and written well enough that it made complete sense to me.

Thanks!

ericpete
Page Editor
0
LVL 61

Expert Comment

by:mbizup
Well written article!

Voted 'yes'.
0

Featured Post

Get your problem seen by more experts

Be seen. Boost your question’s priority for more expert views and faster solutions

Join & Write a Comment

When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.
Basic Overview of office 365 user portal

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month