In the previous article, we introduced the process of slow lookup. Today we will look at how the system handles when looking for an IMP and not finding it. What does the system do?

unrecognized selector sent to instanceError reporting underlying principle

LookUpImpOrForward (import: import: import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import: Import

2, the system creates a forward_IMP function, then uses this function to assign the value forward_IMP to IMP, and then returns the value. So let’s take a look at the implementation of _objc_msgForward_impcache.

3, after a global search, we find that this function is also implemented in assembly, and then we can see the following code:

__objc_msgForward (); __objc_msgForward (); __objc_msgForward ();

5, we translate the following lines of assembly code in detail, the translation result is as follows:

ENTRY __objc_msgForward // Call __objc_forward_handler, Read the return value and store it in register adrp x17, __objc_forward_handler@PAGE // Read the return value of __objc_forward_handler from x17, And then write it to register P17 LDR P17, [x17, __objc_forward_handler@PAGEOFF] // TailCallFunctionPointer is passed in register x17 TailCallFunctionPointer x17 END_ENTRY __objc_msgForwardCopy the code

TailCallFunctionPointer ($0, $0, $0, $0)

.macro TailCallFunctionPointer
    // $0 = function pointer value
    br	$0
.endmacro
Copy the code

__objc_forward_handler under 7, then we will go back to look at the function, because it is the function of the returned directly to the jump $0, we compiled by global search found this function is not implemented, so converted to the C/C + + function name to search, and then send the following function:

void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
Copy the code

8. This function is a reference to objc_defaultForwardHandler, so let’s look at objc_defaultForwardHandler:

9, good familiar with a string of English, let’s first look at the following code:

10. Then we run the code and look at the final result:

11, and found above as if, so we can find, when we call the method, if not, the system is through this process to throw us an exception information, so we can only see here, and then do nothing? No, the system provided a remedy before it got here, which is dynamic method resolution. Let’s explore dynamic method resolution.

Dynamic method resolution

1. Return to the lookUpImpOrForward function, which we’ve described somewhat earlier, and which we’ll focus on today:

We use a simple operation like this to ensure that the operation only happens once, and then we look at the resolveMethod_locked function:

3. In this function, the system first determines whether it is a metaclass, so let’s start to analyze the implementation process of these two branches and the reason why the system is designed this way.

Object method dynamic resolution

ResolveInstanceMethod (resolveInstanceMethod);

2. In this function, the system first obtains an SEL and then determines whether the class implements the SEL. Generally, we do not implement the SEL manually, but in order to prevent the programmer from not implementing the @selector(resolveInstanceMethod) during the development process, the system will cause the program to be unstable. In nsobject. mm, you can find a resolveInstanceMethod.

3. So the above check to see if @selector(resolveInstanceMethod) does not work, and we can see that the system sent a message here. Call resolveInstanceMethod to provide an opportunity for the programmer to make a remedy in this method. Let’s implement this method in the KGTeacher class, and then dynamically add a say666 method to see if the remedy is successful.

4. Then we run the program and look at the output:

5, we can see that the repair is successful, then we continue through the breakpoint, look at where the system sent the message, as follows:

6, we can see that when we implement the resolveInstanceMethod method, we dynamically add to repoint IMP, and we can find IMP during the search process, and we dynamically add.

Class method dynamic resolution

ResolveClassMethod (resolveClassMethod);

2. This function is implemented in a similar way to the dynamic resolution of object methods. Let’s look at the code logic of this function. We can see from the dynamic resolution of the object method above that the system implements this method in nsobject. mm by default, so we can ignore this judgment, and since it is a class method, we make a series of judgments to determine whether the class has been created and whether the +initialize method has been called.

2, resolveClassMethod (); 3, resolveClassMethod (); 4, resolveClassMethod ();

4. Then we run the program and look at the output:

5, we can see that the repair is successful, then we continue through the breakpoint, look at where the system sent the message, as follows:

6. When we implement the resolveClassMethod, we dynamically redirect it to the IMP, and we dynamically add it to the IMP.

7. We can see that after the resolveClassMethod is executed, we make a call to the resolveInstanceMethod function. The reason for this is that apple’s ISA bit is removed, because for the inheritance chain, the class method is stored in the metaclass. And then the metaclass inheritance chain eventually goes to NSObject, so this one goes to resolveInstanceMethod again.

extension

1. As we can see from the above, whether the dynamic resolution of object methods or class methods is made by using resolveInstanceMethod, shall we implement only this one method to achieve the effect of dynamic resolution of object methods and class methods? Let’s try it out. Now that we know that class methods also go because isa goes, let’s go directly to NSObject. So let’s create a class of NSObject, KGObject, and implement the code in that class:

2. Then we run the program and look at the final output:

You can see the effect is a touch. So can we do some bug monitoring in this area? What are the pros and cons?

  • Advantages:

    — Can extract public classes

    — Add monitoring in a non-intrusive way

  • Disadvantages:

    — Increases the performance cost

    — Apple’s message forwarding mechanism failed

conclusion

In this article, we’ve looked at some of the advantages and disadvantages of using this approach, as well as apple’s first chance to find a remedy when a method is called.