IOS martial arts esoteric article summary
Writing in the front
The cache_t cache is about methods. We’ve looked at the cache write process. Before the write process, there are two cache read processes: objc_msgSend and cache_getImp. So what’s the method? It all starts with Runtime…
A possible secret Demo for this section
A, the Runtime
① What is Runtime?
Runtime is a set of APIS written in C, C ++, and assembly that provide a Runtime for OC.
- Runtime: The code runs and loads the executable into memory
- Compile time: the time at which compilation is underway – translating source code will
OC, Swift,
) into machine language (assembly, etc.) and finally into binary
(2) the Runtime version
The Runtime comes in two versions — Legacy and Modern — and the Apple developer documentation is well-documented
-old and __OBJC__ represent Legacy versions, and -new and __OBJC2__ represent Modern versions, so as to make compatibility
③ Function and call of Runtime
Runtime
The underlying compilation provides a setAPI
And for theFrameWork
,Service
use
Runtime call:
Runtime API
, such assel_registerName()
.class_getInstanceSize
NSObject API
, such asisKindOf()
OC
Upper-level mode, such as@selector()
Runtime Runtime Runtime Runtime Runtime Runtime Runtime Runtime Runtime Runtime
Second, the nature of the method
① Research methods
You can see the underlying code and get the essence of the method by compiling it into a CPP file with clang
- Compatible compilation (less code) :
clang -rewrite-objc main.m -o main.cpp
- Full compilation (no error) :
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
orxcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
② Code conversion
((TCJPerson *(*)(id, SEL))(void *)
It is type strong(id)objc_getClass("TCJPerson")
To obtainTCJPerson
Class objectsel_registerName("alloc")
Is equivalent to@selector()
Objc_msgSend (object, method call)
③ The nature of the method
The essence of a method is to send a message via objc_msgSend, where id is the message receiver and SEL is the method number.
Note: If a C function is externally defined and called like void sayHello() {}, it is still sayHello() after clang is compiled instead of being called via objc_msgSend. Because sending messages is the process of finding function implementation, and C functions can be found by function name – pointer.
To verify, passobjc_msgSend
Method to accomplish[person sayHello]
To see if it prints consistently.The printed results are as follows, and they are found to be consistent, so[person sayHello]
Is equivalent toobjc_msgSend(person,sel_registerName("sayHello"))
There are two points to note:
- 1. Direct call
objc_msgSend
, need to import the header file#import <objc/message.h>
- 2, need to
Target --> Build Setting --> search MSG -- change enable strict checking of obc_msgSend calls from YES to NO
, turn off the draconian inspection mechanism, otherwiseobjc_msgSend
The argument to the
④ Send messages to different objects
Subclass TCJTeacher has instance methods sayHello, sayNB and class method sayNC
TCJPerson has instance methods sayHello, sayCode, and class methods sayNA
① Send instance method
Message receiver —Instance objects
② Send the class method
③ Object method call – the actual execution is the implementation of the parent class
Notice the previous detail: The parent TCJPerson implements the sayHello method, while the subclass TCJTeacher does not. Now we can try to have teacher call sayHello to implement the parent class implementation through objc_msgSendSuper.
becauseobjc_msgSend
Cannot send a message to the parent classobjc_msgSendSuper
And giveobjc_super
Structure assignment (inobjc2
All you need to do is assignreceiver
,super_class
)
The receiver –Instance objects
; Super_class –Parent class Class object
Found that no matter[teacher sayHello]
orobjc_msgSendSuper
All execute in the parent classsayHello
So here, we can make a guess:Method calls, first look in the class, if not found in the class, look in the class's parent class
.
④ Send instance methods to the parent class
The receiver –Instance objects
; Super_class –Parent class Class object
Send class methods to the parent class
The receiver –Class object
; Super_class –Superclass metaclass object
Message search process
Message search process is actually through the upper method number SEL send message objc_msgSend find specific imp implementation process
Objc_msgSend is written in sinks. Why C is not written in sinks is because:
The C language
You can’t jump to arbitrary Pointers by writing a function that keeps unknown parameters while assembly has registers- For some functions or operations that are called too frequently, assembly can improve efficiency and performance and be easily recognized by machines
① Start searching
Open theobjc4
Source code, as the main studyArm64 structure
The assembly implementation comes toobjc-msg-arm64.s
, first attached its compilation of the overall implementation of the flow chart
P0 represents the pointer to register 0, x0 represents its value, and w0 represents the lower 32 bits (don’t worry too much).
- 1) start
objc_msgSend
- (2) to determine
Message receiver
If the value is null, the value is returned - (3) to determine
tagged_pointers
(More on that later) - ④ Get the object
isa
A to savep13
In the - 5. According to
isa
formask
The address offset gets the correspondingThe superior object
(Class, metaclass)
See the GetClassFromIsa_p16 definition, which is basically isa & mask to get the class operation
- ⑥ Start searching in the cache
imp
— Started a quick process
② Quick search process
fromCacheLookup
A quick lookup process begins (at this pointx1
issel
.x16
isclass
)
-
In objc_class, the first address is 16 bytes away from the cache, that is, isa is 8 bytes away from the cache, superClass is 8 bytes away from the cache. In the cache, the top 16 bits are used to store mask and the bottom 48 bits are used to store buckets. The p11 = cache
-
Secondly, pull buckets and mask from the cache, and then use mask to calculate the hash subscript according to the hash algorithm
- through
cache
andMask (i.e. 0x0000FFFFFFFFFFFF)
the&
Operation, will be highWipe out the 16-bit mask
,buckets
Pointer address, i.ep10 = buckets
- will
cache
Moves to the right48
A,mask
, i.e.,p11 = mask
- will
objc_msgSend
The parameters of thep1
(that is, the second parameter _cmd)& msak
Through theThe hash algorithm
And get the storage to look forsel-imp
thebucket
The subscriptindex
, i.e.,p12 = index = _cmd & mask
“Why in this way? Because in theStore the sel - imp
Is also through the sameThe hash algorithm computes the hash subscript for storage
, soread
You also need to passRead in the same way
, as shown below
- through
-
③ According to the first address of index and buckets, extract the bucket corresponding to the hash index
- Among them
PTRSHIFT
Is equal to the3
The shift to the leftfour
(i.e.2 ^ 4 = 16
The object of byte) is to compute onebucket
The actual size of the occupied structurebucket_t
In thesel
Account for8
Bytes,imp
Account for8
byte - According to the calculated hash index
index
Multiplied by a singleMemory size occupied by a bucket
,buckets
The first address inReal memory
In theThe offset
- through
Start address + actual offset
Gets the hash subscriptindex
The correspondingbucket
- Among them
-
④ According to the obtained bucket, remove the IMP into P17, that is, P17 = IMP, remove SEL into P9, that is, P9 = SEL
-
⑤ The first recursive loop
- Comparably acquired
bucket
In thesel
与objc_msgSend
Of the second parameter of_cmd
(that is, p1) is equal - If they are equal to each other, jump to
CacheHit
, that is, cache hitimp
- If they are not equal, there are two situations
- If you cannot find it all the time, jump to
CheckMiss
Because the$0
isnormal
, jumps to__objc_msgSend_uncached
, that is, to enterSlow search process
- If according to
index
To obtain thebucket
Is equal to thebuckets
The first element of the, will be artificially presentbucket
Set tobuckets
The last element (passesMove the first address of buckets +mask 44 bits to the right
(equivalent to moving 4 places to the left) directLocate the last element of the bucker
), and continue the recursive loop (The first one
Recursive loop nestingThe second
Recursive loop), namely ⑥ - If the current
bucket
Is not equal tobuckets
The first element of, continuesLook ahead
And into theFirst recursive loop
- If you cannot find it all the time, jump to
- Comparably acquired
-
⑥ The second recursive loop: The only difference is that if the bucket is the first element equivalent to buckets, the bucket goes directly to JumpMiss, where $0 is normal and __objc_msgSend_uncached
Here’s the wholeQuickly find
processThe change of the value
Process flow chart
Slow search process
Slow search – assembly part
In the quick lookup process, if the method implementation is not found, both CheckMiss and JumpMiss end up with the __objc_msgSend_uncached assembly function
-
in
objc-msg-arm64.s
Find in file__objc_msgSend_uncached
The assembly implementation, the core of which isMethodTableLookup
(that is, list of query methods), its source code is as follows -
search
MethodTableLookup
The assembly implementation, the core of which is_lookUpImpOrForward
, assembly source code implementation as follows
The procedure for verifying the above assembly can be verified by assembly debugging
- in
main
, such as[person sayHello]
Object method call with a breakpoint, and enable assembly debuggingDebug -- Debug worlflow -- Always show Disassembly
, run the program - In the assembly
objc_msgSend
Add a breakpoint, perform the break, holdcontrol + stepinto
And into theobjc_msgSend
The assembly of - in
_objc_msgSend_uncached
Add a breakpoint, perform the break, holdcontrol + stepinto
, enter assembly
As you can see from above, lookUpImpOrForward is the last thing that comes up, and it is not an assembly implementation.
Pay attention to
- 1.
C/C++
In the callassembly
To go toFind assembly
.C/C + + call
The method requiresAdd an underscore
- 2,
assembly
In the callC/C++
Method, to findC/C++
Method that needs to be called by the assemblyRemove an underscore
② Slow search -c /C++ part
Follow the instructions in the assembly section to continue the search globallylookUpImpOrForward
And finally theobjc-runtime-new.mm
File found in the source code implementation, this is ac
Implemented functionThe overall slow search process is shown in figure 1The slow process is mainly divided into several steps:
- 1.
cache
To find in the cache, i.eQuickly find
, returns directly if foundimp
, otherwise, enter ② - (2) determine the CLS
- Whether it is
Known class
, if not, thenAn error
- Whether the class
implementation
, if not, it needs to be implemented first to determine the parent class chain. At this time, the purpose of instantiation is to determine the parent class chain, RO, and RW, so as to facilitate the subsequent data reading and search cycle - Whether or not
Initialize the
If not, the system initializes
- Whether it is
- 3.
for
Cycle, according toClass inheritance chain
orMetaclass inheritance chain
Sequential search of- The current
cls
Is used in the list of methodsBinary search algorithm
Find the method, if found, thenThe cache write process is started
(in theIOS: Cache_t analysisAs detailed in this article), and returnsimp
, if not found, returnsnil
- The current
cls
To be an assignmentThe parent class
If the parent class is equal tonil
,Imp = message forwarding
And terminate the recursion into ④ - if
The chain of the parent class
incycle
, an error is reported,Terminate the loop
The parent class cache
Search method in- if
Not found
, directly returnsnil
And continue toLoop through
- if
find
, directly returnsimp
, the implementation ofCache writes
process
- if
- The current
- (4)
judge
Whether it has been executedDynamic method parsing
- if
There is no
, the implementation ofDynamic method parsing
- if
Executed once
Dynamic method resolution, then go toMessage Forwarding Process
- if
The above is the slow search process of the method. The principle of binary search and the detailed steps of the parent class cache search are explained in detail below
GetMethodNoSuper_nolock method: List of binary lookup methods
The process for finding a list of methods is shown below
Its binary search core source code implementation is as follows Algorithm principle
Short description: from the first search, each fetchMiddle position
, and want to findThe value of the key values
In comparison, ifequal
, you need toExclusion classification method
And then the method implementation of the queried location is returned ifNot equal to the
, you need toContinue binary search
, if the loop tocount = 0
If not found, return directlynil
, as follows:
To findTCJPerson
Of the classsayHello
Example method, the binary search process is as follows
Cache_getImp method: superclass cache lookup
cache_getImp
And the way to do that is byAssembly _cache_getImp
Implementation, passed in$0
是 GETIMP
, as shown below
- if
The parent class cache
Is found, jump toCacheHit
If yes, return directlyimp
- If the
The parent class cache
,Could not find
Method, jump toCheckMiss
orJumpMiss
By judgment$0
Jump toLGetImpMiss
, return directlynil
.
conclusion
- for
Object methods (that is, instance methods)
In which theClass lookup
, its slow searchThe chain of the parent class
Is this:Class -- parent class -- root class --nil
- for
Class method
In which theIn the metaclass
, its slow searchThe chain of the parent class
Is this:Metaclass -- root metaclass -- root class --nil
- if
Fast search, slow search
Also did not find a way to implement, then tryDynamic method resolution
- if
Dynamic method resolution
If still not found, proceedforward
Common methods are not implemented error source code
If no implementation is found in the process of fast lookup, slow lookup, or method resolution, message forwarding is used, and the process is as follows
Message forwarding will be implemented
-
Among them
_objc_msgForward_impcache
Is an assembly implementation that jumps to__objc_msgForward
, its core is__objc_forward_handler
-
Assembler implementation to find
__objc_forward_handler
, and not found in the source codeRemove an underscore
Global search_objc_forward_handler
, has the following implementation, which is essentially calledobjc_defaultForwardHandler
methods
If objc_defaultForwardHandler looks familiar, this is the most common error we see in everyday development: not implementing a function, running an application, crashing.
🌰 : defineTCJPerson
Superclass, where there aresayNB
Instance methods andsayHappay
Class method
Define subclasses:TCJStudent
Class with instance methodssayHello
andsayMaster
The class methodsayObjc
, where the instance methodsayMaster
Unrealized.
inmain
In the callTCJStudend
Instance method ofsayMaster
When running the program, an error occurs, indicating that the method is not implemented, as shown below
Now, let’s talk about how to prevent unimplemented crashes of methods before they crash.
Dynamic method analysis
inSlow to find
processNot found
Method is first tried once when implementedDynamic method resolution
, its source code implementation is as follows:It is mainly divided into the following steps
- judge
Whether a class is a metaclass
- If it is
class
, the implementation ofInstance methods
Dynamic method resolutionresolveInstanceMethod
- If it is
The metaclass
, the implementation ofClass method
Dynamic method resolutionresolveClassMethod
, if in a metaclassCould not find
Or forempty
While the,The metaclass
theInstance methods
Dynamic method resolutionresolveInstanceMethod
In the search, mainly becauseClass methods are instance methods in metaclasses
, so you also need to look for dynamic method resolutions for instance methods in the metaclass
- If it is
- if
Dynamic method resolution
, itThe implementation points to other methods
, then continueFinds the specified IMP
, that is, continue the slow searchlookUpImpOrForward
process
The process is as follows
① Example method
forInstance methods
Call, when the implementation of the instance method is not found in both fast and slow lookups, we have a salvage opportunity to try onceDynamic method resolution
, because it isInstance methods
So it goes toresolveInstanceMethod
Method, whose source is as followsIt is mainly divided into the following steps:
- It is sent
resolveInstanceMethod
Before a message, you need to look upcls
Class if there is an implementation of the method, that is, throughlookUpImpOrNil
Methods will come in againlookUpImpOrForward
Slow search process searchresolveInstanceMethod
methods- If not, return directly
- If yes, send it
resolveInstanceMethod
The message
- Again slowly find the implementation of the instance method, that is, through
lookUpImpOrNil
Methods will come in againlookUpImpOrForward
Slow lookup process Lookup instance method
② Crash modification — dynamic method resolution
forExample method say666
Unimplemented error reporting crashes can be passed inclass
The rewriteresolveInstanceMethod
Class method and point it to an implementation of another method, namely, inTCJPerson
The rewriteResolveInstanceMethod class method
That will beExample method say666
Implementation direction ofsayMaster
Method implementation as shown below
If we were in theresolveInstanceMethod
In a class method, it doesn’t point to another method’s implementation, it comes twice. Why is that? We’ll explain later…
(3) class method
forClass method
, like instance methods, can also be overriddenresolveClassMethod
Class method to solve the previous crash problem, namely, inTCJPerson
Class, and willsayNB
The implementation of a class method points to a class methodsayHappy
resolveClassMethod
Class method overrides need to be aware of one thing passed inCLS is no longer a class
.But a metaclass
, can be accessed throughobjc_getMetaClass
Method gets the metaclass of the class becauseBecause class methods are instance methods in metaclasses
.
④ Optimization scheme
Is there a better way to do this once and for all? In fact, it can be found that there are two search paths through the method slow search process
- Instance methods: class — parent class — root class — nil
- Class methods: metaclass — root metaclass — root class — nil
What they all have in common is that if you don’t find them, they all come to youThe root class is NSObject
So can we integrate the above two methods together? The answer is yes, yesNSObject Adds a class
The way toRealize unified processing
And because the class method lookup, in its inheritance chain, lookup is also the instance method, so you can put the unified treatment of instance method and class method inresolveInstanceMethod
Method, as shown belowThe implementation of this way, just with the source code for the method of class processing logic is consistent, that is, the perfect explanation why to call the method of class dynamic method resolution, but also call the object method dynamic method resolution, the fundamental reason is the method of class in the metaclass is instance method.
Above this kind of writing, of course, there will be other problems, such as system method also can be changed, for this, can be optimized, namely we can according to the custom class method unified method name prefix, judging by the prefix is a custom method, and then unified handling custom methods, for example can pop before collapse to the home page, It is mainly used for anti-crash processing of APP online to improve user experience.
⑤ Summary of dynamic method resolution
Instance methods
Can be rewrittenresolveInstanceMethod
addimp
Class method
Can be overridden in this classresolveClassMethod
toThe metaclass
addimp
Or, inNSObject classification
rewriteresolveInstanceMethod
addimp
Dynamic method parsing
As long as you take any steplookUpImpOrNil
Find theimp
I’m not going to look it upThis class
Do the dynamic method resolution, don’t go toNSObjct classification
Dynamic method resolution- All methods are available through the
NSObject classification
rewriteresolveInstanceMethod
addimp
To solve the collapse
Wouldn’t it be nice to process all crashes in NSObjct classification, prefixed to separate business logic? Wrong!
- Unified processing of high coupling degree
- Multiple logical judgments
- May be in
NSObjct classification
The dynamic method resolution has been dealt with before SDK
When encapsulating, you need to give error tolerance
Therefore, the previous ④ optimization scheme is not the most perfect solution. Well, it doesn’t work, it doesn’t work, so what do we do? Don’t worry, Apple dad has a plan for us!
5. Message forwarding mechanism
LookUpImpOrForward: If fast + slow does not find a way to implement it and dynamic resolution does not work, we use message forwarding. However, we find no source code for message forwarding. What methods went through before the method call crashed
- through
instrumentObjcMessageSends
Mode Displays logs about sending messages
instrumentObjcMessageSends
Through lookUpImpOrForward – > log_and_fill_cache – > logMessageSend, found at the bottom of the logMessageSend source instrumentObjcMessageSends source code to achieve, so, In the main call instrumentObjcMessageSends print method call log information, has the following two preparations
-
1, open the objcMsgLogEnabled switch, namely call instrumentObjcMessageSends method, introduced to YES
-
2, in
main
Through theextern
The statementinstrumentObjcMessageSends
methods -
through
logMessageSend
Source code, read the message sent to print information stored in/tmp/msgSends
Directory, as shown below -
Run the code, go to the/TMP /msgSends directory, find a log file that starts with msgSends, open it, and execute the following method before crashing
- Two dynamic method resolutions:
resolveInstanceMethod
methods - Fast forwarding of two messages:
forwardingTargetForSelector
methods - Slow forwarding of two messages:
methodSignatureForSelector + resolveInvocation
- Two dynamic method resolutions:
Fast forwarding process
ForwardingTargetForSelector only a statement in the source code, and no other description, mentioned in the documentation to explain about it:
- The return object of this method is execution
sel
The message will be forwarded to other objects for processing by related methods, but cannot be returnedself
Or you’ll never find it - The method is more efficient, if not implemented, will go to
forwardInvocation:
Method to process - The underlying calls
objc_msgSend(forwardingTarget, sel, ...) ;
To realize the sending of messages - The receiver parameters, return values, etc. of the forwarded message should be the same as the original method
Fast forwarding process resolves crashes
The following code resolves crashes by fast forwarding — i.eTCJPerson
Methods that can’t be implemented, forward toTCJStudent
To implement (forward to objects that already implement the method)
You can also specify no message receiver,Call the method directly from the parent class
, if still not found,Direct error
Slow forwarding process
When the fast forwarding process fails to find the forwarding object, the slow forwarding process startsmethodSignatureForSelector
Follow suit and find it in the help filesmethodSignatureForSelector
Click to viewforwardInvocation
forwardInvocation
andmethodSignatureForSelector
It has to be simultaneous, and the bottom layer will generate one by signing the methodNSInvocation
Pass it as an argument to the call- Lookup can respond
NSInvocation
(This object does not have to be the same for all messages) - use
anInvocation
The message is sent to the object.anInvocation
The results are saved, and the run-time system extracts the results and passes them to the original sender
Slow forwarding process resolution crash
Slow forwarding process
Is the firstmethodSignatureForSelector
Provide a method signature and thenforwardInvocation
Based on theNSInvocation
To realize the forwarding of messages
You could be rightforwardInvocation
In the methodinvocation
No processing, and no crash error
So, from the above, the program does not crash whether the forwardInvocation method handles the invocation transaction or not.
Through the Hopper /IDA disassembly message forwarding mechanism
Hopper and IDA are tools that help us statically analyze visibility files, disassemble executable files into pseudocode, control flow diagrams, and so on, using Hopper as an example.
-
Run program crash, view stack information
-
found
___forwarding___
fromCoreFoundation
-
through
image list
, read the entire image file, and then searchCoreFoundation
To view the path to its executable file -
Go to the file path
CoreFoundation
Executable file -
Open the
hopper
, the choice ofTry the Demo
Then drag in the executable from the previous stephopper
To disassemble, selectx86(64 bits)
-
The following is the interface after disassembly, mainly using the above three functions, respectively assembly, flow chart, pseudocode
-
Search for __forwarding_prep_0___ through the search box on the left, then select the pseudocode
-
The following is a
__forwarding_prep_0___
Assembler pseudocode, jump to___forwarding___
-
The following is a
___forwarding___
Pseudo-code implementation, the first is to see if it is implementedforwardingTargetForSelector
If there is no response, jump toloc_6459b
That is, fast forwarding does not respond and the slow forwarding process enters -
Jump to
loc_6459b
Below it to determine whether to respondmethodSignatureForSelector
methods -
If there is no response, jump to loc_6490b, an error is reported
-
If you get
methodSignatureForSelector
The method is signed asnil
, is also a direct error -
if
methodSignatureForSelector
If the return value is not empty, theforwardInvocation
Method forinvocation
For processing
Through the above two search methods can be verified, there are three methods of message forwarding
- Fast forward forwardingTargetForSelector 】
- [Slow forwarding]
- methodSignatureForSelector
- forwardInvocation
The overall process of message forwarding is as follows
The processing of message forwarding is divided into two parts:
- [Fast forwarding] When slow search and dynamic method resolution are not found, message forwarding is carried out. First, fast message forwarding is carried out, that is, go to
forwardingTargetForSelector
methods- If the return
Message receiver
If no method implementation is found in the message receiver, another method lookup process is entered - If nil is returned, slow message forwarding is entered
- If the return
- [Slow forwarding] To
methodSignatureForSelector
methods- If the returned
The method signature
fornil
The directCollapse of the error
- If the returned
The method signature
Don’t fornil
And went toforwardInvocation
Method, rightinvocation
Transaction processing,No error is reported if no processing is performed
- If the returned
Why are dynamic method resolutions executed twice?
The dynamic method resolution method mentioned in the previous article has been implemented twice, with the following two ways of analysis
Explore god’s perspective
In the slow search process, we learned thatresolveInstanceMethod
Method is executed throughlookUpImpOrForward --> resolveMethod_locked --> resolveInstanceMethod
Came toresolveInstanceMethod
Source code, in the source code by sendingresolve_sel
Message firing, as shown belowSo you can findresolveInstanceMethod
In the methodIMP imp = lookUpImpOrNil(inst, sel, cls);
A breakpoint is added to passbt
printThe stack information
So what’s going on
-
in
resolveInstanceMethod
In the methodIMP imp = lookUpImpOrNil(inst, sel, cls);
Add a breakpoint and run the program until the first “come” passesbt
To viewFirst dynamic method resolution
Stack information at this timesel
issay666
-
Continue until the second “coming” print, looking at the stack information, in the second, we can see that is through
CoreFoundation
the-[NSObject(NSObject) methodSignatureForSelector:]
Method, and then throughclass_getInstanceMethod
Enter dynamic method resolution again -
We need to look at the stack information from the previous step
CoreFoundation
What did The Chinese do? throughHopper
The disassemblyCoreFoundation
The executable file to viewmethodSignatureForSelector
Method pseudocode -
through
methodSignatureForSelector
Pseudocode entry___methodDescriptionForSelector
The implementation of the -
Enter the
___methodDescriptionForSelector
The pseudo-code implementation, combined with the assembled stack print, can be seen in___methodDescriptionForSelector
That’s called in this methodObjc4 source
theclass_getInstanceMethod
-
in
Objc4-818.2 -
Source searchclass_getInstanceMethod
, the source code implementation is shown below
This can be verified by code debugging, as shown below, inclass_getInstanceMethod
Add a breakpoint to the method and executemethodSignatureForSelector
After the method, the signature is returned, indicating that the method signature is in effect, and apple is walking toinvocation
Before, developers were given a chance to query again, so go toclass_getInstanceMethod
Here, go to the method query againsay666
And then it goes to the dynamic method resolution again
Therefore, the above analysis also confirms the reason why the resolveInstanceMethod method was executed twice
Exploration without a God’s perspective
If there is no God perspective, we can also use the code to deduce where the dynamic method resolution is called again
TCJPerson
Class to rewriteresolveInstanceMethod
Method, and plusclass_addMethod
Operating theAssignment IMP
At this time,resolveInstanceMethod
Will you go twice?
If an IMP is assigned, the dynamic method resolution will only go once, indicating that it is not the second dynamic method resolution
Keep exploring
- To get rid of
resolveInstanceMethod
MethodIMP
In theTCJPerson
Class to rewriteforwardingTargetForSelector
Method and specify that the return value is[TCJStudent alloc]
, rerun ifresolveInstanceMethod
It was printed twice, which means it wasforwardingTargetForSelector
Method executes the dynamic method resolution before, and vice versaforwardingTargetForSelector
Methods after
Results found resolveInstanceMethod in print or print only once, that means the second resolution after forwardingTargetForSelector method dynamic method
- in
TCJPerson
Class to rewritemethodSignatureForSelector
和forwardInvocation
Run,
The results showed that the second method of dynamic resolution between methodSignatureForSelector and forwardInvocation method.
The second analysis also supports the foregoingresolveInstanceMethod
The reason why it was executed twice. After the above argument, we know that in the process of slow message forwarding, inmethodSignatureForSelector
和 forwardInvocation
There is also a dynamic method resolution between methods, which Apple gives a chance to do again, as shown in the figure below
Write in the back
So far, the objc_msgSend message sending process has been analyzed and summarized here
[Quick search process]
First, in the classBuffer cache
To find the implementation of the specified methodSlow search process
If it is not found in the cache, theClass method list
If still not found, go toA list of caches and methods for the parent class chain
Look for[Dynamic Method resolution]
If a slow search doesn’t find anything,First chance to fix it
Just try it onceDynamic method resolution
, that is, to rewrite theresolveInstanceMethod/resolveClassMethod
methods[Message forwarding]
If the dynamic method resolution is still not found, proceedforward
There are two remedial opportunities in message forwarding:Fast forwarding + Slow forwarding
- If not, the program will crash
unrecognized selector sent to instance
Finally, learn harmoniously without being impatient. I’m still me, a different color of fireworks.