preface
Methods occupy an important position in programming, we are familiar with methods and strange. What is familiar is that it is used every day, and what is strange is that everyone has only a half-understanding of the underlying implementation. The quick search process and slow search process of the method were explored before, and the search process at the bottom of the method had a certain understanding. If neither the fast lookup process nor the slow lookup process has a method lookup implementation, what will the following process look like, and Apple will give a chance to dynamic method resolution
The preparatory work
- Objc4-818.2 – the source code
- Iced 🍉
Case analysis
Create a LWPerson class and declare a sayHello method that is not implemented
int main(int argc, char * argv[]) {
@autoreleasepool {
LWPerson * perosn = [LWPerson alloc];
[perosn sayHello];
}
return 0;
}
Copy the code
DoesNotRecognizeSelector or unrecognized selector sent to instance doesNotRecognizeSelector doesNotRecognizeSelector Search in the source code to find the implementation of the underlying source code, in the method search process if the last IMP or not found, will call forward_IMP
Forward_imp = _objc_msgForward_impcache, source code view under _objc_msgForward_impcache implementation, global search _objc_msgForward_impcache
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward / / jump __objc_msgForward
END_ENTRY __objc_msgForward_impcache
ENTRY __objc_msgForward
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
...
.macro TailCallFunctionPointer
// $0 = function pointer value
br $0 / / jump imp
.endmacro
...
Copy the code
__objc_msgForward_impcache
The bottom layer is assembly implementation, the main codeb __objc_msgForward
__objc_msgForward
In theTailCallFunctionPointer
Is a macro, previously explored is jumpimp
.x17
The register storesimp
As can be seen from the compilationx17
What matters is that__objc_forward_handler
- Global search
__objc_forward_handler
If there is no concrete implementation in the assembly, then it is not in the assemblyC/C++
Source code, global searchobjc_forward_handler
, the source code is as follows
// Default forward handler halts the process.
__attribute__((noreturn, cold)) void
objc_defaultForwardHandler(id self, SEL sel)
{
_objc_fatal("%c[%s %s]: unrecognized selector sent to instance %p "
"(no message forward handler is installed)".class_isMetaClass(object_getClass(self)) ? '+' : The '-'.object_getClassName(self), sel_getName(sel), self);
}
void *_objc_forward_handler = (void*)objc_defaultForwardHandler;
Copy the code
Error reported in _objc_FATAL familiar with the classic crash message error method is not implemented
In the fast and slow search process did not find IMP, do not directly crash, do not give a chance. No must give a chance, or I am not convinced, the system or stem but I ha, give a chance is dynamic method resolution
Dynamic method resolution
In lookUpImpOrForward, if an IMP is not found, the process resolveMethod_locked is dynamically resolved
NEVER_INLINE
IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior){...// No implementation found. Try method resolver once.
// If the query method is not implemented, the system attempts a method resolution
if (slowpath(behavior & LOOKUP_RESOLVER)) {
behavior ^= LOOKUP_RESOLVER;
// Dynamic method resolution
return resolveMethod_locked(inst, sel, cls, behavior); }... }Copy the code
ResolveMethod_locked dynamic method resolution is locked
static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertLocked(a);ASSERT(cls->isRealized());
runtimeLock.unlock(a);// Determine whether the CLS class is a metaclass, if the class specification indicates that the instance method is called
if (! cls->isMetaClass()) {
// try [cls resolveInstanceMethod:sel]
resolveInstanceMethod(inst, sel, cls);
}
else { // If it is a metaclass, the class method is called
// try [nonMetaClass resolveClassMethod:sel]
// and [cls resolveInstanceMethod:sel]
resolveClassMethod(inst, sel, cls);
// If not, look in the object method of the metaclass, which is equivalent to the object method in the metaclass
if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
resolveInstanceMethod(inst, sel, cls); }}// chances are that calling the resolver have populated the cache
// so attempt using it
// The imp that corresponds to sel fast lookup and sel slow lookup is actually retrieved from the cache, because it has been cached previously
return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}
Copy the code
- In the first place to judge
cls
Whether it isThe metaclass
- If it is not
The metaclass
Just a normal class, then the instance method of the call jumpsresolveInstanceMethod
process - If it is
The metaclass
, then the class method jump is calledresolveClassMethod
process lookUpImpOrForwardTryCache
Fast lookup and slow lookupsel
The correspondingimp
And then returnimp
resolveInstanceMethod
methods
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{ // inST object // CLS class
runtimeLock.assertUnlocked(a);ASSERT(cls->isRealized());
SEL resolve_sel = @selector(resolveInstanceMethod:);
// As long as the CLS metaclass initialization resolve_sel method must be implemented because NSObject implements resolveInstanceMethod by default
// The purpose is to cache the resolveInstanceMethod method into the CLS metaclass
Resolve_sel is a class method based on lookUpImpOrNilTryCache
if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
// Resolver not implemented.
return;
}
// Call the resolveInstanceMethod method to send a message
// Send the message via objc_msgSend to a CLS method
BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
// check whether the resolve_sel method is implemented
bool resolved = msg(cls, resolve_sel, sel);
// Why lookUpImpOrNilTryCache is called
// The resolveInstanceMethod method is called. But it does not necessarily implement sel methods
Imp = forward_imp = forward_imp = forward_imp
// imp = forward_IMP goes down and down_unlock if the slow lookup process dynamic resolution method has passed
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
// Resolved and IMP are dynamically added
if (resolved && PrintResolving) {
if (imp) {
...
}
else {
// Method resolver didn't add anything?. }}}Copy the code
- First create
resolveInstanceMethod
theSEL resolve_sel
- According to the
lookUpImpOrNilTryCache (cls, resolve_sel, cls->ISA(true))
knowresolveInstanceMethod
Are class methods that are found by fast and slow lookup processesresolve_sel
The correspondingimp
The cacheresolveInstanceMethod
methods - Directly through
msg(cls, resolve_sel, sel)
Send a message to the class, as you can see hereresolveInstanceMethod
Is a class method
LookUpImpOrNilTryCache (INST, SEL, CLS) Fast and slow lookup processes
- through
lookUpImpOrNilTryCache
To determine theresolveInstanceMethod
Is there an implementation in the methodsel
The correspondingimp
- If it’s implemented, it’s not in cache, go in
lookUpImpOrForward
Find thesel
The correspondingimp
Insert cache, callimp
End of the search process - If it’s not implemented, it’s not in cache, enter
lookUpImpOrForward
Look for,sel
I didn’t find a matchimp
At this time,imp
=forward_imp
.Dynamic method resolution
It’s only called once, and it goesdone_unlock
anddone
The process,sel
andforward_imp
The cache is inserted for message forwarding
resolveInstanceMethod
methods
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
runtimeLock.assertUnlocked(a);ASSERT(cls->isRealized());
ASSERT(cls->isMetaClass());
// inST class // CLS metaclass
NSObject implements the resolveClassMethod method by default
if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
// Resolver not implemented.
return;
}
/ / return to class
Class nonmeta;
{
mutex_locker_t lock(runtimeLock);
nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
// +initialize path should have realized nonmeta already
if(! nonmeta->isRealized()) {
_objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
nonmeta->nameForLogging(), nonmeta); }}BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
// Send a message to the class
bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
// Class methods are equivalent to instance methods in metaclasses, both fast and slow look-ups
IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
if (resolved && PrintResolving) {
if (imp) {
...
}
else {
// Method resolver didn't add anything?. }}}Copy the code
resolveClassMethod
inNSobject
As long as the metaclass initialization is done, the purpose is to cache in the metaclass- call
resolveClassMethod
Class method, the purpose of which is to make possibleResolveClassMethod dynamically implements SEL corresponding IMP
imp = lookUpImpOrNilTryCache(inst, sel, cls)
The cachesel
The correspondingimp
, no matterimp
There is no dynamic addition, if there is no cacheforward_imp
lookUpImpOrNilTryCache
methods
LookUpImpOrNilTryCache method name, which looks for IMP or nil as far as possible by querying the cache, LookUpImpOrNilTryCache is called in both the resolveInstanceMethod and resolveClassMethod methods
extern IMP lookUpImpOrNilTryCache(id obj, SEL, Class cls, int behavior = 0);
IMP lookUpImpOrNilTryCache(id inst, SEL sel, Class cls, int behavior)
{
/ / LOOKUP_NIL = 4 no parameter behaviors = 0 0 | 4 = 4
return _lookUpImpTryCache(inst, sel, cls, behavior | LOOKUP_NIL);
}
Copy the code
First of all, the last parameter is the default behaviors = 0, LOOKUP_NIL = 4, behaviors | LOOKUP_NIL greater than or equal to LOOKUP_NIL
ALWAYS_INLINE
static IMP _lookUpImpTryCache(id inst, SEL sel, Class cls, int behavior)
{
runtimeLock.assertUnlocked(a);// Whether the CLS is initialized
if (slowpath(! cls->isInitialized())) {
// Look up lookUpImpOrForward without it being initialized
return lookUpImpOrForward(inst, sel, cls, behavior);
}
// Find sel imp in cache
IMP imp = cache_getImp(cls, sel);
// IMP has a value to enter the done process
if(imp ! =NULL) goto done;
#if CONFIG_USE_PREOPT_CACHES
// Whether there is a shared cache
if (fastpath(cls->cache.isConstantOptimizedCache(/* strict */true))) {
imp = cache_getImp(cls->cache.`preoptFallbackClass(), sel); } `#endif
// Imp is not found in the cache and enters the slow search process
// behavior = 4, 4&2 = 0
if (slowpath(imp == NULL)) {
return lookUpImpOrForward(inst, sel, cls, behavior);
}
done:
//(behavior & LOOKUP_NIL) = 4 & 4 = 1
//LOOKUP_NIL just writes to the cache with _objc_msgForward_impcache
if ((behavior & LOOKUP_NIL) && imp == (IMP)_objc_msgForward_impcache) {
return nil;
}
return imp;
}
Copy the code
To determine whether the CLS is initialized, it will be initialized
Find in cache
- Look it up in the cache
sel
The correspondingimp
- if
imp
There is jumpdone
process - Check whether there is a shared cache for the underlying system library
- If no query is found in the cache
imp
To enter the slow search process
Slow search process
- In the slow search process,
behavior
=4
,4
&2
=0
Go into dynamic method resolution, so it doesn’t loop all the time - The most important if not query to this point
imp
=forward_imp
Jump,lookUpImpOrForward
In thedone_unlock
anddone
Process, insert cache, returnforward_imp
The done process
done
Process: (behavior
&LOOKUP_NIL
) andimp
=_objc_msgForward_impcache
, if the cache isforward_imp
, and return directlynil
, otherwise returnimp
.LOOKUP_NIL
The condition is to find if it’s dynamically addedimp
And then there is theimp
Inserted into the cache
LookUpImpOrNilTryCache uses LOOKUP_NIL to control insertion into the cache, whether sel imp is implemented or not, and if IMP returns a value, it must be implemented dynamically in a dynamic method resolution
resolveInstanceMethod
Instance to explore
implementationresolveInstanceMethod
methods
Add the resolveInstanceMethod method to the LWPerson class
int main(int argc, const char * argv[]) {
@autoreleasepool {
LWPerson * person = [LWPerson alloc];
[person sayHello];
}
return 0;
}
Copy the code
@implementation LWPerson
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"- into the % @ -".NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
@end
Copy the code
202107 -- 03 12:57:22.582805+0800 KCObjcBuild[7949:330351[sayHello--202107 -- 03 12:57:22.583624+0800 KCObjcBuild[7949:330351[sayHello--Copy the code
The resolveInstanceMethod method was actually called before the crash
The resolution system automatically sends a message to the resolveInstanceMethod (resolveInstanceMethod).
The first time you call the stack of resolveInstanceMethod, you can see that you are following the dynamic resolution method of the slow lookup process
The stack information of resolveInstanceMethod is called the second time, which is called by CoreFoundation, the underlying system library. After the message forwarding is completed, the slow search process is started again, and the dynamic method resolution is entered, and the stack information of resolveInstanceMethod is called again, so there are two times in total. The detailed flow of the second call is described in more detail later
Dynamically addsayHello
methods
@implementation LWPerson
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"- into the % @ -".NSStringFromSelector(sel));
if (@selector(sayHello) == sel) {
IMP imp = class_getMethodImplementation(self , @selector(sayHello2));
Method meth = class_getInstanceMethod(self , @selector(sayHello2));
const char * type = method_getTypeEncoding(meth);
return class_addMethod(self ,sel, imp, type);;
}
return [super resolveInstanceMethod:sel];
}
- (void)sayHello2{
NSLog(@"--%s---",__func__);
}
@end
Copy the code
resolveInstanceMethod
Only called once, because it is added dynamicallysayHello
methodslookUpImpOrForwardTryCache
Direct access toimp
, directly callimp
, the search process ends- The crash also resolves the dynamic method resolution system to give it a chance
- Specific process:
resolveMethod_locked
–>resolveInstanceMethod
— > callresolveInstanceMethod
–>lookUpImpOrNilTryCache(inst, sel, cls)
–>lookUpImpOrForwardTryCache
— > callimp
resolveClassMethod
Instance to explore
implementationresolveClassMethod
methods
Add the resolveInstanceMethod method to the LWPerson class without dynamically implementing the test method
int main(int argc, const char * argv[]) {
@autoreleasepool {
LWPerson * person = [LWPerson alloc];
[LWPerson test];
}
return 0;
}
Copy the code
@implementation LWPerson
+(BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"- into the % @ -".NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
@end
Copy the code
202107 -- 03 13:51:22.784897+0800 KCObjcBuild[8483:359482] -- enter test--202107 -- 03 13:51:22.785774+0800 KCObjcBuild[8483:359482] -- enter test--Copy the code
- It did call just before the crash
resolveClassMethod
Method, and called twice, the logical sum of two callsresolveInstanceMethod
Method calls are the same twice - call
resolveClassMethod
In the future, I’ll look it uplookUpImpOrNilTryCache
There is no concrete dynamic implementationsel
The correspondingimp
In the metaclass cache at this timesel
The correspondingimp
theimp
isforward_imp
.lookUpImpOrNilTryCache
There’s a judgment straight back in therenil
At this point directly toresolveInstanceMethod
Because class methods are actually instance methods in metaclass - If it doesn’t happen
lookUpImpOrForwardTryCache
Access to theforward_imp
The message forwarding process is displayed
Dynamically addtest
methods
+(BOOL)resolveClassMethod:(SEL)sel{
if (@selector(test) == sel) {
NSLog(@"ResolveClassMethod - enter the % @ -".NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(object_getClass([self class]), @selector(newTest));
Method meth = class_getClassMethod(object_getClass([self class]) , @selector(newTest));
const char * type = method_getTypeEncoding(meth);
return class_addMethod(object_getClass([self class]) ,sel, imp, type);;
}
return[super resolveClassMethod:sel]; } + (void)newTest{
NSLog(@"--%s---",__func__);
}
Copy the code
202107 -- 03 16:14:04.262688+0800 KCObjcBuild[9964:434208] resolveClassMethod-- enter test--202107 -- 03 16:14:04.263331+0800 KCObjcBuild[9964:434208] --+[LWPerson newTest]---
Copy the code
resolveClassMethod
Only called once, because it is added dynamicallytest
methodsresolveClassMethod
andresolveInstanceMethod
The call process is basically the same ifresolveClassMethod
No query was called onceresolveInstanceMethod
call
resolveClassMethod
special
Since the call to resolveClassMethod is not found, and the call to resolveInstanceMethod is found, both resolveClassMethod and resolveInstanceMethod should be implemented in LWPerson class. Theoretically any method can be called
int main(int argc, const char * argv[]) {
@autoreleasepool {
LWPerson * person = [LWPerson alloc];
[LWPerson test];
}
return 0;
}
Copy the code
@implementation LWPerson
+(BOOL)resolveClassMethod:(SEL)sel{
NSLog(@"ResolveClassMethod - enter the % @ -".NSStringFromSelector(sel));
return [super resolveClassMethod:sel];
}
+(BOOL)resolveInstanceMethod:(SEL)sel{
NSLog(@"ResolveInstanceMethod - enter the % @ -".NSStringFromSelector(sel));
return [super resolveInstanceMethod:sel];
}
@end
Copy the code
202107 -- 03 14:52:55.076902+0800 KCObjcBuild[9158:391254] resolveClassMethod-- enter test--202107 -- 03 14:52:55.077890+0800 KCObjcBuild[9158:391254] resolveClassMethod-- enter test--Copy the code
The result is different from what we expected. Only the resolveClassMethod in LWPerson class is called, but the resolveInstanceMethod method of LWPerson class is not called. The source code shows that the resolveInstanceMethod method is called, so trace the source code
inst
isLWPerson
Class,cls
isLWPerson
Of the classThe metaclass
LookUpImpOrNilTryCache the argument CLS ->ISA(true) is the root class that enters lookUpImpOrNilTryCache
At this point, INST is the metaclass of LWPerson class, and CLS is the root class. Fast lookup and slow lookup refer to the root metaclass, which means that the metaclass calls the instance method
msg(cls, resolve_sel, sel)
And you can verify thatobjc_msgSend
Send messages without distinction-
and+
Methods.objc_msgSend
The receiver ofcls
isThe metaclass
Means likeThe metaclass
Send a message, the message lookup will findA metaclass
To find out, soresolveInstanceMethod
inThe metaclass
Is called, so in the classresolveInstanceMethod
Method will not be called, not thatThe metaclass
andclass
The name is the same, but the address is different
Create NSObject + LW and add the resolveInstanceMethod method to the class, because the parent of the root metaclass is the root class. The root metaclass cannot be found in the root class, so only the root class can be used because the root metaclass cannot be created
@implementation NSObject (LW)
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (@selector(test) == sel) {
NSLog(@"ResolveInstanceMethod - enter the % @ -".NSStringFromSelector(sel));
}
return NO;
}
@end
Copy the code
202107 -- 03 16:00:21.999137+0800 KCObjcBuild[9755:425282] resolveClassMethod-- enter test--202107 -- 03 16:00:22.000222+0800 KCObjcBuild[9755:425282] resolveInstanceMethod-- enter test--202107 -- 03 16:00:22.000579+0800 KCObjcBuild[9755:425282] resolveClassMethod-- enter test--202107 -- 03 16:00:22.000681+0800 KCObjcBuild[9755:425282] resolveInstanceMethod-- enter test--Copy the code
Call resolveClassMethod and then resolveInstanceMethod twice
integration
Dynamic method resolution
If no class method is dynamically added to the resolveClassMethod method, the resolveInstanceMethod in the metaclass is called. Write resolveInstanceMethod to a public class that calls both class methods and instance methods
- Instance method lookup flow:
object
–>class
— > untilThe root class
(NSObject
) — – >nil
- Class method lookup flow:
class
–>The metaclass
— > untilThe root class
(NSObject
) — – >nil
You end up in the NSObject class, so this common class is the NSObject class
@implementation NSObject (LW)
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (@selector(sayHello) == sel) {
NSLog(@"- into the % @ -".NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(self , @selector(sayHello2));
Method meth = class_getInstanceMethod(self , @selector(sayHello2));
const char * type = method_getTypeEncoding(meth);
return class_addMethod(self ,sel, imp, type);;
}else if (@selector(test) == sel){
NSLog(@"- into the % @ -".NSStringFromSelector(sel));
IMP imp = class_getMethodImplementation(object_getClass([self class]), @selector(newTest));
Method meth = class_getClassMethod(object_getClass([self class]) , @selector(newTest));
const char * type = method_getTypeEncoding(meth);
return class_addMethod(object_getClass([self class]) ,sel, imp, type);;
}
return NO;
}
- (void)sayHello2{
NSLog(@"--%s---",__func__); } + (void)newTest{
NSLog(@"--%s---",__func__);
}
@end
Copy the code
202107 -- 03 16:54:56.295564+0800 KCObjcBuild[10394:453264[sayHello--202107 -- 03 16:54:56.296146+0800 KCObjcBuild[10394:453264] - [NSObject(LW) sayHello2]---
202107 -- 03 16:54:56.296443+0800 KCObjcBuild[10394:453264] -- enter test--202107 -- 03 16:54:56.296615+0800 KCObjcBuild[10394:453264+ [] -NSObject(LW) newTest]---
Copy the code
Instance methods are class method calls, and the system automatically calls the resolveInstanceMethod method, which is consistent with the above exploration. Dynamic methods determine the advantages
- You can handle method crashes in a unified manner. You can report method crashes to the server or jump to the home page
- If you have different modules in your project you can make business distinctions by naming them differently
- This approach is called faceted programming
AOP
Differences between AOP and OOP
OOP
: In fact, it is the encapsulation of the properties and behaviors of the object. The same functions are extracted and encapsulated separately, with strong dependence and high couplingAOP
: is the processing of a step and stage, from which the extraction of the section, there is repeated operation behavior, AOP can be extracted, the use of dynamic proxy, achieve unified maintenance of program functions, dependence is small, the coupling degree is small, aloneAOP
The removal of extracted functionality also has no impact on the main code.AOP
More like a three-dimensional vertical axis, each class in the plane has a common logical passageAOP
Concatenated, the various classes in the plane itself have no relation
Follow the flow chart
forward
The fast and slow lookup process is not found, and the dynamic resolution method is not found, so the following will enter the message forwarding process, but the relevant source code is not found in objC4-818.2, the source code provided by CoreFunction is not detailed query. Apple still provides logging assistance
Log auxiliary
LookUpImpOrForward –> log_and_fill_cache –> logMessageSend, enter logMessageSend to see the source code
if (slowpath(objcMsgLogEnabled && implementer)) {
bool cacheIt = logMessageSend(implementer->isMetaClass(),
cls->nameForLogging(),
implementer->nameForLogging(),
sel);
if(! cacheIt)return;
}
Copy the code
LogMessageSend can call objcMsgLogEnabled must be YES
bool objcMsgLogEnabled = false;
static int objcMsgLogFD = - 1;
bool logMessageSend(bool isClassMethod,
const char *objectsClass,
const char *implementingClass,
SEL selector)
{
char buf[ 1024 ];
// Create/open the log file
if (objcMsgLogFD == (- 1))
{
// The path to the file
snprintf (buf, sizeof(buf), "/tmp/msgSends-%d", (int) getpid ());
objcMsgLogFD = secure_open (buf, O_WRONLY | O_CREAT, geteuid());
if (objcMsgLogFD < 0) {
// no log file - disable logging
objcMsgLogEnabled = false;
objcMsgLogFD = - 1;
return true; }}// Make the log entry
snprintf(buf, sizeof(buf), "%c %s %s %s\n",
isClassMethod ? '+' : The '-',
objectsClass,
implementingClass,
sel_getName(selector));
objcMsgLogLock.lock(a);write (objcMsgLogFD, buf, strlen(buf));
objcMsgLogLock.unlock(a);// Tell caller to not cache the method
return false;
}
Copy the code
/ TMP /msgSends is a sandbox path for saving logs. After this parameter is enabled, go to the sandbox path to obtain the file. The default objcMsgLogEnabled = false so find the assignment
void instrumentObjcMessageSends(BOOL flag)
{
bool enable = flag;
// Shortcut NOP
if (objcMsgLogEnabled == enable)
return;
// If enabling, flush all method caches so we get some traces
if (enable)
_objc_flush_caches(Nil);
// Sync our log file
if(objcMsgLogFD ! =- 1)
fsync (objcMsgLogFD);
objcMsgLogEnabled = enable;
}
Copy the code
Through the instrumentObjcMessageSends to objcMsgLogEnabled assignment, So in need to log information statement instrumentObjcMessageSends extern void instrumentObjcMessageSends (BOOL flag);
extern void instrumentObjcMessageSends(BOOL flag);
int main(int argc, const char * argv[]) {
@autoreleasepool {
LWPerson * p = [LWPerson alloc];
instrumentObjcMessageSends(YES);
[p sayHello];
instrumentObjcMessageSends(NO);
}
return 0;
}
Copy the code
The message forwarding process following the dynamic resolution method isforwardingTargetForSelector
andmethodSignatureForSelector
The message forwarding process will be explored later
conclusion
Dynamic method decisions give you one more chance, giving you a chance to implement dynamically, and giving developers a chance to try something new. It has to be said that the process of dynamic method resolution is complex, and it needs careful experience to make it more clear