Going through Apache Cordova's source code, I ran into two lines of code that I'm puzzled about:
//[obj performSelector:normalSelector withObject:command];
objc_msgSend(obj,normalSelector,command);
From Apple's documentation, there doesn't seem to be a lot of difference between these two methods.
id objc_msgSend(id theReceiver, SEL theSelector, ...)
Sends a message with a simple return value to an instance of a class.
- (id)performSelector:(SEL)aSelectorwithObject:(id)anObject
Sends a message to the receiver with an object as the argument. (required)
What exactly is the difference between these two methods? In the case above, both are sending messages with an object as an argument to a receiving object.
You're asking the difference between two "methods" but only one of them is actually a method. The objc_msgSend function is, well, a function. Not a method.
The objc_msgSend function is the function that you actually call when you invoke any method on any object in Objective C. For example, the following two are basically equivalent:
// This is what the compiler generates
objc_msgSend(obj, #selector(sel:), param);
// This is what you write
[obj sel:param];
// You can check the assembly output, they are *almost* identical!
The major difference here is that objc_msgSend does not get type checked by the compiler -- or at least, its arguments don't get type checked against the selector's parameter types. So the following are roughly equivalent:
[obj performSelector:normalSelector withObject:command];
objc_msgSend(obj, #selector(performSelector:withObject:),
normalSelector, command);
But, that's a bit of a waste, since all performSelector:withObject: does is call objc_msgSend.
HOWEVER: You should stay away from obc_msgSend because it is not type-safe, as mentioned above. All the apache devs are doing is removing a single method call, which will only give you very slight performance benefits in most cases.
The commented out line is correct, the objc_msgSend() line is incorrect in that it needs to be explicitly typed (varargs are not compatible with non-varargs function calls on some platforms sometimes).
Effectively they do the same thing. Really, the method call version is just a wrapper around objc_msgSend().
Related
I'm starting to learn Objective-C, and wondering what happens if you pass in an object to a dynamic call to a method, where the method doesn't accept any.
#import <Foundation/Foundation.h>
# pragma mark Forward declarations
#class DynamicWorker;
#class DynamicExecutor;
# pragma mark Interfaces
// Protocol for a worker object, not receiving any parameters
#protocol Worker<NSObject>
-(void)doStuff;
#end
// Dynamic worker returns a selector to a private method capable of
// doing work.
#interface DynamicWorker : NSObject<Worker>
- (SEL)getWorkDynamically;
#end
// Dynamic executor calls a worker with a selector it provided. The
// executor passes itself, in case the worker needs to launch more
// workers. The method signature should be of the form
// (void)method:(DynamicExecutor *)executor
#interface DynamicExecutor : NSObject
- (void)runWorker:(id<Worker>)worker withSelector:(SEL)selector;
#end
#pragma mark Implementations
#implementation DynamicWorker;
- (SEL)getWorkDynamically {
return #selector(doStuff);
}
-(void) doStuff {
NSLog(#"I don't accept parameters");
}
#end
#implementation DynamicExecutor;
// Here I get a warning, that I choose to ignore now:
// https://stackoverflow.com/q/7017281/946814
- (void)runWorker:(id<Worker>)worker withSelector:(SEL)selector {
[worker performSelector:selector withObject:self];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSLog(#"Getting busy");
DynamicWorker *worker = [[DynamicWorker alloc] init];
DynamicExecutor *executor = [[DynamicExecutor alloc] init];
[executor runWorker:worker withSelector:[worker getWorkDynamically]];
}
return 0;
}
So far, it doesn't seem to cause any issues, and in fact looks similar to Javascript event handlers, where accepting the event is optional. From my understanding of the bare metal, though, I believe the argument would be placed on the stack, and have no idea how the runtime would know that it should be discarded.
From my understanding of the bare metal, though, I believe the argument would be placed on the stack, and have no idea how the runtime would know that it should be discarded.
You are correct, the caller places the argument on the stack. After the call returns the caller removes the arguments it placed on the stack, so discarding any extra arguments the callee doesn't expect is not an issue.
However that is not enough to know your code will work, the callee needs to know where the arguments are on the stack. The stack usually grows downwards as items are pushed onto it and the callee locates arguments as positive offsets from the stack pointer. If arguments are pushed left-to-right then the last argument is at the smallest offset from the stack pointer, the first at the largest offset. If additional arguments are pushed in this scenario then the offsets to the expected arguments would all change. However (Objective-)C supports variadic functions, those that take an unspecified number of arguments (think printf, stringWithFormat:, etc.), and so arguments in a call are pushed right-to-left, at least for variadic functions, so that the first argument is the last pushed and hence at a known constant offset from the stack pointer regardless of how many arguments are pushed.
Finally an Objective-C method call is translated into a call to a runtime function, objc_msgSend(), which implements the dynamic method lookup. This function is variadic (as different messages take different numbers of arguments).
So your Objective-C method call becomes a call to a variadic runtime function, and if you supply too many arguments they are ignored by the callee and cleared up by the caller.
Hope all that makes sense!
Addendum
In the comments #newacct has correctly pointed out that objc_msgSend is not variadic; I should have written "effectively variadic" as I was blurring details for simplicity. They also argued that it is a "trampoline" and not a function; while this is technically correct a trampoline is essentially a function which jumps to other code rather than returning directly, that other code doing the return back to the caller (this is similar to what tail call optimisation does).
Back to "essentially variadic": The objc_msgSend function, like all functions which implement Objective-C methods, take a first argument which is the object reference the method is being called on, a second argument which is the selector of the desired method, and then in order any arguments the method takes - so the call takes a variable number of arguments but is not strictly a variadic function.
To locate the actual method implementation to invoke at runtime objc_msgSend uses the first two arguments; the object reference and the selector; and performs a lookup. When it locates the appropriate implementation it jumps/tail calls/trampolines to it. As objc_msgSend cannot know how many arguments have been passed until it has examined the selector, which is the second argument, it needs to be able to locate the second argument at a known offset from the stack pointer, and for this to (easily) possible arguments must be pushed in reverse order - just as with a variadic function. As the arguments are pushed by the caller in reverse order they have no impact on the callee and additional ones will be ignored and harmless provided the caller is responsible for removing the arguments after the call.
For variadic functions the caller has to be the one which removes the arguments, as only it knows how many are passed, for non-variadic functions the callee could remove the arguments - and this includes the callee that objc_msgSend tail calls - but many compilers, including Clang, have the caller remove them.
So the call to objc_msgSend, which is the compilation of a method call, will under Clang ignore any extra arguments by essentially the same mechanism as variadic functions do.
Hope that makes it clearer and doesn't add confusion!
(Note: In practice some arguments may be passed in registers and not on the stack, this does not have significant impact on the above description.)
You are calling the method -[NSObject performSelector:withObject:], whose documentation says
aSelector should identify a method that takes a single argument of
type id.
So what you are violating the contract of the API.
If you look at the source code of the -[NSObject performSelector:], -[NSObject performSelector:withObject:], and -[NSObject performSelector:withObject:withObject:] methods in the Objective-C runtime, they are all just simple wrappers around objc_msgSend -- each of them casts objc_msgSend to the function type that the implementation of the method with the appropriate number of id parameters would have. They are able to perform these casts because they assume that you are passing a selector corresponding to a function with the appropriate number of id parameters, as specified in the documentation.
When you call objc_msgSend, you must call it as if it had the type of the underlying implementing function of the method that is being called. That is because objc_msgSend is a trampoline written in assembly that calls the implementing function with all the registers and stack space for arguments exactly the same as in the call to objc_msgSend, so the caller must set up the arguments exactly as expected by the callee (the underlying implementing function). The way to do that is to cast objc_msgSend to the function pointer type that the implementing function of the method would have (considering its parameter and return types), and then make the call using that.
For all effects and purposes, we can consider a call to objc_msgSend the same as a call directly to the underlying implementing function (i.e. we can consider ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj) as the same as ((id(*)(id, SEL, id))[self methodForSelector:sel])(self, sel, obj)). So the question of using performSelector:withObject: with a method with less arguments basically reduces down to: Is it safe to call a C function with a function pointer of type that has more parameters than the function actually has (i.e. the function pointer type has all the parameters the function has, with the same types, but it has additional ones at the end)?
The general answer to this, according to the C standard, is that No, it is undefined behavior to call a function with a function pointer of a different type. For example, see C99 standard section 6.5.2.2 paragraph 9:
If the function is defined with a type that is not compatible with the
type (of the expression) pointed to by the expression that denotes the
called function, the behavior is undefined.
For all the platforms that Objective-C is used on, however (32-bit and 64-bit x86; 32-bit and 64-bit ARM), I believe the function calling convention is such that it is safe for a caller to set up a function call with more arguments than the callee expects, and the extra passed arguments will simply be ignored (the callee won't know they are there, but that doesn't have any negative effects; even if the callee uses the registers and stack space for those extra arguments for other things, a callee is allowed to do that anyway). I have not examined the ABIs in detail but I believe that this is true.
However, if Objective-C is ported to a new platform, you will need to examine the function calling convention for that platform to determine whether a caller making a call with more parameters than the callee expects will cause any problems on that platform. You cannot just assume that it will work on all platforms.
I've inferred what a lot of things DO in Objective-C, and I've gone through several tutorials that simply talk about the data types, but I haven't run across anything that simply explains the syntax.
For starters, what does this mean? What it does is start a thread and get data returned from a server:
- (void)apiCall:(void (^)(NSMutableArray *list))block {
Does something in that function header tell me that it is asynchronous? is that what block means?
No, block doesn't mean asynchronous, a block in Obj-C is just a bit of code that can be passed as an argument to a method.
methods that start with - are instance methods and those that start with + are class methods.
^ is a syntactic marker to denote a block.
For your first question: you would have to look at the API documentation to find out if it is asynchronous.
For more information about blocks in general, see here:
Apple Blocks Programming Guide
Let's start with your second bullet:
Class methods are declared with +, instance methods are declared with -.
The first and third are related, the parameter named block is a code block, it's a piece of code intended to be run later. Given the name of this method apiCall, I suggest this being the method run after the call is done.
It would we natural to suspect that this method will do some work on another thread and then invoke the block you supplied, but for this you'd need to check the documentation or the code.
The signature: (void (^)(NSMutableArray* list)) block describes a code block with a void return type and a NSMutableArray* list as only parameter.
An example usage of the block parameter would be:
void (^apiCallCallback)(NSMutableArray*) = ^(NSMutableArray* list) {
NSLog(#"The API returned %d items in a list", [list length]);
}
[someApiInstance apiCall:apiCallCallback];
After the API instance is done doing whatever it is suppose to do, you'll see that the log statement is printed.
I have a C struct that contains a function pointer. Now, I have used this setup within C with no problems, but now I'm using this C struct in Objective-C and I need to pass a function (or selector) pointer that is defined in the Objective-C class.
1. Here is what I have for the Objective-C selector that needs to be passed as a pointer to the C function:
- (void)myObjCSelector:(int*)myIntArray
{
// Do whatever I need with myIntArray
}
2. And here is where I run into a wall, Within Objective-C I'm trying to pass the selector as a pointer to the C function call: In place of "myObjCSelectorPointer" I need the proper syntax to pass the selector as a function pointer in this C function call:
passObjCSelectorPointerToCContext(cContextReference, myObjCSelectorPointer);
I did investigate this issue, but could mainly find several different ways of doing similar things, but I couldn't find anything specific for calling C functions and passing an Objective-C selector pointer.
In objc a selector is not a function pointer. A selector is a unique integer that is mapped to a string in a method lookup table stored by the objc runtime. In the above case your method name would be myObjCSelector: and to get the unique selector for it you would type #selector(myObjCSelector:). However this would be of no use to you because it doesnt represent a particular implementation of a function.
What youre looking for is IMP. Refer to this SO question.
EDIT 2:
IMP myObjCSelectorPointer = (void (*)(id,SEL,int*))[self methodForSelector:#selector(myObjCSelector:)];
Then you can call the method using
myObjCSelectorPointer(self,#selector(myObjCSelector:),myIntArray);
However, what this means you will need to make sure that you add the pointer to self in the c function call passObjCSelectorPointerToCContext.
So it should look like this
passObjCSelectorPointerToCContext(cContextReference, self, myObjCSelectorPointer);
when called from within the object that contains the method.
It is important to note though that using IMP is almost never the right technique. You should try to stick with pure Obj-C. Obj-C is quite efficient after the first call to a message because it uses temporal caching.
EDIT 1:
It's useful to understand why objc works in this way. The Apple documents explain it in depth. However a short explanation is as follows:
When you send a message to an object such as [myobject somemethod] the compiler won't immediately know which particular implementation of somemethod to call because there might be multiple classes with multiple overriden versions of somemethod. All of those methods have the same selector, irrespective of its arguments and return values and hence the decision about which implementation of somemethod is deffered to when the program is running. [myobject somemethod] gets converted by the compiler into a C function call:
objc_msgSend(myobject, #selector(somemethod))
This is a special function that searches each myobject class layout to see whether that class knows how to respond to a somemethod message. If not it then searches that class's parent and so on until the root. If none of the classes can respond to somemethod then NSObject defines a private method called forward where all unknown messages are sent.
Assuming that a class can respond to the somemethod message then it will also have a particular pointer of type IMP that points to the actual implementation of the method. At that point the method will be called.
There is considerably more to this procedure than I have described but the outline should be enough to help you understand what the goal of a selector is.
One final point is that the reason method names are mapped to unique integers via the #selector directive is so that the runtime doesn't have to waste time doing string comparisons.
Basically, the answer is: Objective-C selectors are different from function pointers. You need two pieces of data to perform a selector. That is an object and the selector itself. You will need some glue to accomplish your task.
Check this question.
Do you have to use a function pointer? In Objective-C, you can get the function pointer to an arbitrary method implementation (known as an IMP), but this is extremely uncommon, and usually not a good idea. Calling objc_msgSend() directly is also not the greatest idea, because there are several different variants of objc_msgSend(), and the compiler automatically chooses different ones to use based on the return type of the method. Methods that return an object go through objc_msgSend(), but objects that return structs might go through objc_msgSend() or they might go through objc_msgSend_stret(). And if the method returns a double, then it goes through objc_msgSend_fpret()...
Documentation: Objective-C Runtime Reference: Sending Messages
Instead, I might recommend using a target-action pair, or using a block. Then you might do something like:
myContextRef->target = anObjcObject;
myContextRef->action = #selector(invokeMe:);
And when you're done, do:
[myContextRef->target performSelector:myContextRef->action withObject:someReturnInformation];
Or maybe use a block:
myContextRef->completionHandler = [^(id returnInformation) {
[anObjcObject invokeMe:returnInformation];
} copy];
And then when you're done, do:
myContextRef->completionHandler(someReturnInformation);
(and don't forget to -release the block when you free the context)
I am fairly new to Objective-C. Currently porting my own library from C#/Java to objective C.
I now run into a very strange problem for me.
I have a NSArray with several Note objects. I want to transpose on of these notes:
//Note.h
- (Note *) transpose: (int) semitones;
//Main
NSArray *notes = [get it from somewhere];
Note *transposedNote = [[notes objectAtIndex:0]transpose:1]; //Doesn't compile
Note *transposedNote = [(Note*)[notes objectAtIndex:0]transpose:1]//Does compile
Is this happening because there is already a transpose method available in the general libraries?
I thought due to the dynamic nature of objective-C at runtime it would be checked which class objectAtIndex returns and then sends the message to it?
It is my understanding that there is no runtime type checking for the assignment operator in Objective C. Since an array can contain a mixture of types, there is no way for the system to know what objectAtIndex returns.
How about
Note *transposedNote = [notes objectAtIndex:0]; // first line
[transposedNote transpose:1]; // second line
? Notice in the reference that objectAtIndex: returns an id, you will see it is pretty obvious:
In the code above, because id can fit into any object, the first line doesn't need to cast it into Note. In the second line I'm just calling a method on a Note so the compiler is happy.
In your code you are calling methods on the returned id object, so the compiler doesn't understand what you are trying to do. Just assign it to a Note reference and it will be fine.
Yes, the error is because there's already a transpose: method in AppKit. And you're also right that it normally doesn't cause an error when you have two unrelated classes implementing methods with the same name. The reason you get an error is because the two methods either return incompatible types or take incompatible types as arguments. In your particular case, you're seeing both problems:
-[NSResponder transpose:] takes an id and returns void
-[Note transpose:] takes an int and returns an id
These are totally incompatible types, and the compiler does need to know the types involved even if it doesn't know what exact method is going to be called.
It does compile unless you have -Werror set to treat warnings as errors.
It might produce a warning if the compiler doesn't already know about the selector or if the selector is declared in more than one class. In the former case, it should be necessary only to import the interface containing the selector. In the latter case, you'll need to do the cast to suppress the error.
I want to add scripting support for an Objective-C project using the objc runtime. Now I face the problem, that I don't have a clue, how I should call an Objective-C method which takes several named arguments.
So for example the following objective-c call
[object foo:bar];
could be called from C with:
objc_msgSend(object, sel_getUid("foo:"), bar);
But how would I do something similar for the method call:
[object foo:var bar:var2 err:errVar];
??
Best Markus
The accepted answer is close, but it won't work properly for certain types. For example, if the method is declared to take a float as its second argument, this won't work.
To properly use objc_msgSend, you have to cast it to the the appropriate type. For example, if your method is declared as
- (void)foo:(id)foo bar:(float)bar err:(NSError **)err
then you would need to do something like this:
void (*objc_msgSendTyped)(id self, SEL _cmd, id foo, float bar, NSError**error) = (void*)objc_msgSend;
objc_msgSendTyped(self, #selector(foo:bar:err:), foo, bar, error);
Try the above case with just objc_msgSend, and log out the received arguments. You won't see the correct values in the called function. This unusual casting situation arises because objc_msgSend is not intended to be called like a normal C function. It is (and must be) implemented in assembly, and just jumps to a target C function after fiddling with a few registers. In particular, there is no consistent way to refer to any argument past the first two from within objc_msgSend.
Another case where just calling objc_msgSend straight wouldn't work is a method that returns an NSRect, say, because objc_msgSend is not used in that case, objc_msgSend_stret is. In the underlying C function for a method that returns an NSRect, the first argument is actually a pointer to an out value NSRect, and the function itself actually returns void. You must match this convention when calling because it's what the called method will assume. Further, the circumstances in which objc_msgSend_stret is used differ between architectures. There is also an objc_msgSend_fpret, which should be used for methods that return certain floating point types on certain architectures.
Now, since you're trying to do a scripting bridge thing, you probably cannot explicitly cast every case you run across, you want a general solution. All in all, this is not completely trivial, and unfortunately your code has to be specialized to each architecture you wish to target (e.g. i386, x86_64, ppc). Your best bet is probably to see how PyObjC does it. You'll also want to take a look at libffi. It's probably a good idea to understand a little bit more about how parameters are passed in C, which you can read about in the Mac OS X ABI Guide. Last, Greg Parker, who works on the objc runtime, has written a bunch of very nice posts on objc internals.
objc_msgSend(object, sel_getUid("foo:bar:err:"), var, var2, errVar);
If one of the variables is a float, you need to use #Ken's method, or cheat by a reinterpret-cast:
objc_msgSend(..., *(int*)&var, ...)
Also, if the selector returns a float, you may need to use objc_msgSend_fpret, and if it returns a struct you must use objc_msgSend_stret. If that is a call to superclass you need to use objc_msgSendSuper2.
objc_msgSend(obj, #selector(foo:bar:err:), var, var2, &errVar);