Ah, the Objective-C runtime. One of the most overlooked features of the language, and yet perhaps the most important part. It’s the glue that enables the dynamism and expressibility of a Smalltalk-like messaging model on top of C; it’s what makes Objective-C so great.
In practical terms, it’s rare for an iOS or OS X developer to need to dive down to the runtime level — in most cases, I’d say it’s actually a code smell — but it’s still worth having a basic understanding of how things work under the hood. I’m not going to give you a general overview of the runtime, but rather explore a specific edge case I encountered recently while working on a testing framework.
The meat-and-potatoes of the Objective-C runtime is
objc_msgSend, the C function responsible for sending a message to an object. When you write the following line of Objective-C code:
The compiler actually translates it down into the following C function call:
There are a million interesting things to say about
objc_msgSend and how it works, but today I want to focus on an interesting edge case. The return type of
id: what happens if you need to evaluate a method that returns a primitive or C struct rather than something expressible as an object pointer?
Executing the following contrived example returns a compiler error because of the type mismatch:
If you wrote that second line as
[view frame], the compiler wouldn’t actually turn that into an
objc_msgSend call, but rather invoke a lesser-known member of the
objc_msgSend_stret is an alternate version of
objc_msgSend that exists solely to return a struct value (the ‘stret’ stands for for struct return). Other related functions exist for other primitive types, such as
objc_msgSend_fret for floating-point numbers.
Trying to manually call
objc_msgSend_stret isn’t as simple as its more-common bigger brother. Even the initial act of figuring out its method signature is confusing: compare the signature suggested by Xcode’s autocompletion with the signature given in the runtime documentation:
Inline Documentation Viewer
If you look at its declaration in
objc/message.h, it becomes clear that the true signature is the former. But this line of code will also trigger a compiler error.
objc_msgSend_stret has a return type of void, which is obviously not a CGRect. And you can’t just cast the return value to a CGRect and call it a day.
The problem is that
objc_msgSend_stret exists to do a task that isn’t expressible in C (mind blown, right?). It defines a function that returns an arbitrary C struct, whose type won’t be known until runtime; C doesn’t have a way to express that for non-pointer types.
The solution? You need to cast the
objc_msgSend_stret function itself into a function whose return type is the struct you’re expecting. Here is a complete, functioning version of our example:
Pretty crazy, right?