objective-c
Objective-c extends the C language and joins the object-oriented and Smalltalk messaging mechanisms. The core of this extension is a runtime library written in C and compiled languages. It is the cornerstone of Objective-c object-oriented and dynamic mechanism.
Objective-c is a dynamic language, which means that it requires not only a compiler, but also a runtime system to dynamically create classes and objects, deliver messages, and forward them. Understanding Objective-c's Runtime mechanism can help us understand the language better, expand the language when appropriate, and address some design or technical issues in the project from a system level. To understand the Runtime, first understand its core-message passing (Messaging).
Message Delivery (Messaging)
I ' m sorry that I long ago coined the term "objects" for this topic because it gets many people to focus on the lesser idea . The big idea is "messaging" –that are what the kernal[sic] of Smalltalk are all about ... The key in making great and growable systems are much more to design how it modules communicate rather than what their int Ernal properties and behaviors should be.
Alan Kay has repeatedly stressed that the core of Smalltalk is not object-oriented, object-oriented is just the lesser ideas, message delivery is the big idea.
In many languages, such as C, invoking a method is actually jumping to a point in memory and starting to execute a piece of code. There is no dynamic feature, because this is determined at compile time. In Objective-c, the [object Foo] syntax does not immediately execute the code of the Foo method. It is a message that sends an object called Foo at run time. This message, which may be handled by object, may be forwarded to another object, or it can be ignored to pretend that the message was confiscated. Many different messages can also be implemented with the same method. These are all determined when the program is running.
In fact, the syntax of the OBJECTIVE-C function call that you write at compile time will be translated into a function call of C-objc_msgsend (). For example, the following two lines of code are equivalent:
1 [array insertObject:foo atIndex:5];
2
3 objc_msgSend(array, @selector(insertObject:atIndex:), foo, 5);
The key to messaging is hidden in the ISA pointer in Objc_object and the class dispatch table in Objc_class.
Objc_object, Objc_class and Ojbc_method
In Objective-c, classes, objects, and methods are all structures of C, and from the objc/objc.h header file, we can find their definitions:
1 struct objc_object {
2 Class isa OBJC_ISA_AVAILABILITY;
3 };
4
5 struct objc_class {
6 Class isa OBJC_ISA_AVAILABILITY;
7 #if !__OBJC2__
8 Class super_class;
9 const char *name;
10 long version;
11 long info;
12 long instance_size;
13 struct objc_ivar_list *ivars;
14 **struct objc_method_list **methodLists**;
15 **struct objc_cache *cache**;
16 struct objc_protocol_list *protocols;
17 #endif
18 };
19
20 struct objc_method_list {
21 struct objc_method_list *obsolete;
22 int method_count;
23
24 #ifdef __LP64__
25 int space;
26 #endif
27
28 /* variable length structure */
29 struct objc_method method_list[1];
30 };
31
32 struct objc_method {
33 SEL method_name;
34 char *method_types; /* a string representing argument/return types */
35 IMP method_imp;
36 };
The Objc_method_list essence is an array of variable lengths with Objc_method elements. A objc_method struct has a function name, the SEL, a string representing the type of the function (see Type Encoding), and the implementation of the function Imp.
From these definitions, you can see that sending a message also objc_msgsend doing something. For example, Objc_msgsend (obj, foo):
1. First, find its class by using the ISA pointer of obj;
2. Find Foo in the class method list;
3. If the class does not reach Foo, continue to find it in the superclass;
4. Once you have found the function foo, go to implement Imp.
But there is a problem with this implementation, which is inefficient. But a class often has only 20% of functions that are often called, which can account for 80% of the total number of calls. It is not reasonable for each message to traverse one objc_method_list at a time. If you cache a function that is often called, it can greatly improve the efficiency of the function query. This is what another important member of the Objc_class Objc_cache do-after finding Foo, the method_name of Foo is the key and Method_imp is stored as value. When you receive the Foo message again, you can find it directly in the cache and avoid traversing the objc_method_list.
Dynamic method parsing and forwarding
In the example above, what happens if Foo doesn't find it? Normally, the program hangs up at run time and throws unrecognized selector sent to ... The exception. But before the exception is thrown, the Objective-c runtime will give you three chance to save the program:
1.Method resolution
2.Fast forwarding
3. Normal forwarding
Method Resolution
First, the OBJECTIVE-C runtime calls +resolveinstancemethod: or +resolveclassmethod:, giving you a chance to provide a function implementation. If you add a function and return YES, the runtime restarts the message sending process. or foo, for example, you can do this:
1 void fooMethod(id obj, SEL _cmd)
2 {
3 NSLog(@"Doing foo");
4 }
5
6 + (BOOL)resolveInstanceMethod:(SEL)aSEL
7 {
8 if(aSEL == @selector(foo:)){
9 class_addMethod([self class], aSEL, (IMP)fooMethod, "[email protected]:");
10 return YES;
11 }
12 return [super resolveInstanceMethod];
13 }
Core Data is useful to this approach. The getter and setter of the properties in Nsmanagedobjects is dynamically added at run time.
If the Resolve method returns NO, the runtime moves to the next step: Message Forwarding (msg Forwarding).
Ps:ios 4.3 Adds a number of new runtime methods, primarily the method of the IMP prefix, such as Imp_implementationwithblock () to quickly create an IMP with block. The above example can be rewritten as:
1 IMP fooIMP = imp_implementationWithBlock(^(id _self) {
2 NSLog(@"Doing foo");
3 });
4
5 class_addMethod([self class], aSEL, fooIMP, "[email protected]:");
Fast forwarding
If the target object implements-forwardingtargetforselector: The Runtime then invokes this method, giving you the opportunity to forward the message to other objects.
1 - (id)forwardingTargetForSelector:(SEL)aSelector
2 {
3 if(aSelector == @selector(foo:)){
4 return alternateObject;
5 }
6 return [super forwardingTargetForSelector:aSelector];
7 }
As long as this method returns not nil and self, the entire message sending process is restarted and of course the sent object becomes the object you return. Otherwise, you will continue to Normal fowarding.
This is called Fast, just to differentiate the next forwarding mechanism. Because this step does not create any new objects, the next forwarding creates a Nsinvocation object, so it is relatively faster.
Normal forwarding
This step is the last time the Runtime gives you a chance to save. First it sends-methodsignatureforselector: The message gets the parameters of the function and the return value type. If-methodsignatureforselector: Return nil, Runtime will issue-doesnotrecognizeselector: message, and the program will then hang up. If a function signature is returned, Runtime creates a Nsinvocation object and sends a-forwardinvocation: message to the target object.
Nsinvocation is actually a description of a message, including information such as selector and parameters. So you can modify the incoming Nsinvocation object in-forwardinvocation: And then send the-invokewithtarget: message to it, and pass in a new target:
1 - (void)forwardInvocation:(NSInvocation *)invocation
2 {
3 SEL sel = invocation.selector;
4
5 if([alternateObject respondsToSelector:sel]) {
6 [invocation invokeWithTarget:alternateObject];
7 }
8 else {
9 [self doesNotRecognizeSelector:sel];
10 }
11 }
Many places in Cocoa have used messaging mechanisms to extend language, such as Proxies, Nsundomanager, and Responder Chain. Nsproxy is designed to be used as a proxy for forwarding messages, Nsundomanager intercepts a message and sends it, and Responder Chain guarantees that a message is forwarded to the appropriate responder.
Summarize
Sending a message to an object in Objective-c follows several steps:
1. Try to find the message in the dispatch table of the object class.
2. If found, jump to the corresponding function imp to execute the implementation code; If not found, Runtime will send +resolveinstancemethod: or +resolveclassmethod: Try to resolve the message;
3. If the Resolve method returns No,runtime, send-forwardingtargetforselector: Allows you to forward this message to another object;
4. If no new target object is returned, Runtime sends-methodsignatureforselector: and-forwardinvocation: Message. You can send-invokewithtarget: messages to forward messages manually or send-doesnotrecognizeselector: throws an exception.
Using the Objective-c runtime feature, we can expand our language to address some of the design and technical issues in project development. In the next article, I will introduce method swizzling technology and how to use method swizzling to do Logging.
Reference
Message forwarding
Objective-c-messaging
The Faster Objc_msgsend
Understanding OBJECTIVE-C Runtime
Objective-c Runtime