preface

The previous article described the process of finding a method, but what happens when it can’t be found? Yes, it’s an error. The program crashed. Let’s simply repeat that we declare an object method and a class method but do not implement them, as shown in the figure below:Call the object method, as shown:Call the class method, as shown:The above two unimplemented method calls both caused the program to crash, respectively:

  • -[Person sayWork]: unrecognized selector sent to instance 0x108b27bc0
  • +[Person sayHappy]: unrecognized selector sent to class 0x100008208

How do you avoid crashes caused by this approach not being implemented? Let’s keep exploring.

First, the root causes of the collapse

reviewOk,In thelookUpImpOrForwardWhen thecurClassEmpty timeimp = forward_imp;And out of the loop, as shown in the figure:In this case, the return isforward_imp. inlookUpImpOrForwardWe defined it at the beginningforward_impAs shown in figure:So we found it_objc_msgForward_impcachethisimp, that is:whenimpThe system calls it if it cannot find it_objc_msgForward_impcache.

Enter the_objc_msgForward_impcache, found no implementation, as shown in the figure:Global search, find the implementation in assembly, as shown:After a simple process found__objc_forward_handler, find again, and realize as shown in the figure:So the source of the error was found.

Dynamic method resolution

To avoid this error, to make our application more robust, we have to handle this error, and iOS is not so rigid, so we have to find a way to handle this error. Go back tolookUpImpOrForward, it is not difficult to find some processing after the end of the loop, as shown in figure:This is a one-time method that will enter the judgment if it is not found the first time and then modify itbehaviorValue ensures that the judgment condition is not satisfied when entering the judgment next time.

Proof: jump in assemblylookUpImpOrForwardWhen,x3The value of3, i.e.,lookUpImpOrForwardThe third parameter of3As shown in figure:lookUpImpOrForwardIn the viewbehaviorThe value of is3As shown in figure: LOOKUP_RESOLVERThe value of2As shown in figure:So the first judgment is zero3 & 2, i.e.,10 & 11 = 10A value of2True, and thenbehavior ^= LOOKUP_RESOLVER;.behaviorThe value of10 ^ 11for01Is passed in as a parameterresolveMethod_locked, will pass laterlookUpImpOrForwardTryCacheand_lookUpImpTryCacheOnce again,calllookUpImpOrForwardAnd the incomingbehavior, the judgment condition is1 & 2, i.e.,01 & 10A false value does not satisfy the condition.

So no implementation is found after the return_objc_msgForward_impcacheWe did some other fault-tolerant processing on our system, and this isresolveMethod_locked“, let’s dig deeper.resolveMethod_lockedThe implementation is shown in figure:So let’s look at the return, and by return we enterlookUpImpOrForwardTryCacheAs shown in figure:_lookUpImpTryCacheAs shown in figure:Because in themsgSendI started to verifycls, so it must meet the judgment condition to re-enterlookUpImpOrForward. So does the method just go around and look for it again? Obviously not a waste of resources, so let’s go back toresolveMethod_locked, as shown in the figure above, before returninglookUpImpOrForwardTryCacheI made a judgment before, that the way in the branch isresolveInstanceMethodandresolveClassMethod.

resolveInstanceMethodThe implementation is shown as follows:Figure out if there is an implementation in the current classresolveInstanceMethodIs called and passed the currentselThe main business here has been dealt with.

resolveClassMethodAs shown in figure:It’s a little bit more computation than the instance method, because the class method is stored in the metaclass, so I get the metaclass.

summary

After the previous exploration, we know that implementing the resolveInstanceMethod and resolveClassMethod methods in a class can intercept information about the current crash.

We implement in implementationresolveInstanceMethodAnd calls the unimplementedsayWorkAs shown in figure:Here we areresolveInstanceMethodThe midpoint breakpoint, and it prints outsayWork“, so we managed to intercept it before reporting an error. I’m going to dynamically insert one hereimpwithsayWorkCorresponding, can not report the error, the operation is as follows:We made itsayWorkAnd put thesayOktheimpwithsayWorktheselAssociation. Continue down, as shown below:

It printed out successfullysayOk.

In the same wayresolveClassMethodAs shown in figure:So we successfully handled the unimplemented crash of the method through dynamic method resolution.