iOS interview questions 2018 (3)

Messaging, iOS interview questions 2018, latest iOS interview questions, iOS objective c interview questions
This post explores messaging within Objective-C. Messaging is the terminology for invoking methods on an object. The format for a message expression is as follows (the brackets are required): [object…

Chapter 3: Messaging

This post explores messaging within Objective-C. Messaging is the terminology for invoking methods on an object. The format for a message expression is as follows (the brackets are required):

[object method]
or in Objective-C parlance
[receiver message]
Here’s a simple example:
// Create an instance of SomeClass object
SomeClass *ptr = [[SomeClass alloc] init];
// Send the message ‘printInstanceVars’ to the ‘ptr’ receiver
[ptr printInstanceVars];

Every Objective-C object has a class, and every Objective-C class has a list of methods. Each method has a selector, a function pointer to the implementation, and some metadata. The job of objc_msgSend is to take the object and selector that's passed in, look up the corresponding method's function pointer, and then jump to that function pointer.

Looking up a method can be extremely complicated. If a method isn’t found on a class, then it needs to continue searching in the superclasses. If no method is found at all, then it needs to call into the runtime’s message forwarding code. If this is the very first message being sent to a particular class, then it has to call that class’s +initialize method.

How lookup process works

Each class has a cache which stores methods as pairs of selectors and function pointers, known in Objective-C as IMPs. They're organized as a hash table so lookups are fast. When looking up a method, the runtime first consults the cache. If the method isn't in the cache, it follows the slow, complicated procedure, and then places the result into the cache so that the next time can be fast.

What objc_msgSend method does?

  1. Get the class of the object passed in.
  2. Get the method cache of that class.
  3. Use the selector passed in to look up the method in the cache.
  4. If it’s not in the cache, call into the C code.
  5. Jump to the IMP for the method.

When a new object is created, memory for it is allocated, and its instance variables are initialized. First among the object’s variables is a pointer to its class structure. This pointer, called isa, gives the object access to its class and, through the class, to all the classes it inherits from.

Messaging Framework

When a message is sent to an object, the messaging function follows the object’s isa pointer to the class structure where it looks up the method selector in the dispatch table. If it can’t find the selector there, objc_msgSend follows the pointer to the superclass and tries to find the selector in its dispatch table. Successive failures cause objc_msgSend to climb the class hierarchy until it reaches the NSObject class. Once it locates the selector, the function calls the method entered in the table and passes it the receiving object’s data structure.

This is the way that method implementations are chosen at runtime — or, in the jargon of object-oriented programming, that methods are dynamically bound to messages.

Message Forwarding

What is message forwarding?

Simply speaking, it allows unknown messages to be trapped and reacted to. In other words, any time an unknown message is sent, it gets delivered to your code in a nice package, at which point you can do whatever you like with it.

What Happens?

What happens when you do [customeDate tommorowDate] and customeDatedoesn't implement a tommorowDatemethod? When it does implement such a method, it's pretty straightforward: it looks up the appropriate method, then jumps to it. When no such method can be found, a complicated sequence of events ensues:

  1. Lazy method resolution. This is done by sending resolveInstanceMethod: (resolveClassMethod:for class methods) to the class in question. If that method returns YES, the message send is restarted under the assumption that the appropriate method has now been added.
  2. Fast forwarding path. This is done by sending forwardingTargetForSelector: to the target, if it implements it. If it implements this method and it returns something other than nil or self, the whole message sending process is restarted with that return value as the new target.
  3. Normal forwarding path. First the runtime will send methodSignatureForSelector: to see what kind of argument and return types are present. If a method signature is returned, the runtime creates an NSInvocation describing the message being sent and then sends forwardInvocation: to the object. If no method signature is found, the runtime sends doesNotRecognizeSelector:.

Lazy Resolution

Doing this allows for really fast “forwarding”, because after the method is resolved, it gets invoked as part of the normal message sending process. This kind of thing is great for stuff like @dynamic properties. Plug it in to the class using +resolveInstanceMethod: and off you go.

Fast Forwarding

This technique is great for faking multiple inheritence. You can write a little override like this:

- (id)forwardingTargetForSelector:(SEL)sel { return _otherObject; }

This will cause any unknown message to be sent to _otherObject, which will make your object appear from the outside as though it combined your object with this other object in one.

Normal Forwarding

The first two are basically just optimizations that allow forwarding to go faster. If you don’t take advantage of them, the full forwarding mechanism goes into action. This creates an NSInvocation object which fully encapsulates the message being sent. It holds the target, the selector, and all of the arguments. It also allows full control over the return value.

Before the runtime can build the NSInvocation it needs an NSMethodSignature, so it requests one using -methodSignatureForSelector:. This is required due to Objective-C's C heritage. In order to bundle the arguments up in the NSInvocation, the runtime needs to know what kind of arguments there are, and how many of them there are. This information isn't normally provided in a C runtime environment, so it has to do an end run around the C "bag of bytes" view of the world and get that type information in another way.

Once the invocation is constructed, the runtime then invokes your forwardInvocation: method. From there you can do whatever you want with the invocation it hands you. The possibilities are endless.

Conclusion

It’s always interesting to dive into framework internals. objc_msgSend in particular is a work of art. Message forwarding is a powerful technique that greatly multiplies the expressiveness of Objective-C. Cocoa uses it for things like NSUndoManager and distributed objects, and it can let you do a lot of nifty things in your own code.

Hope this article is useful for people looking to gain knowledge as well as clear interview, Please ❤️ to recommend this post to others 😊. Let me know your feedback. :)