Debug analysis
The OC method call in the end is how it? Check the information before know oc method call is called by the form of messages sent, then to explore what combination of source code
LGPerson *person = [[LGPerson alloc] init];
Class pClass = [LGPerson class];
[person sayNB];
View compilation process (Note the use of device debugging)
Debug - Debug workflow - Always show Disassembly
0x100000b4b <+27>: movq 0x746(%rip), %rsi ; (void *)0x00000001000012d8: LGPerson
0x100000b52 <+34>: movq 0x71f(%rip), %rcx ; "alloc"
0x100000b59 <+41>: movq %rsi, %rdi
0x100000b5c <+44>: movq %rcx, %rsi
0x100000b5f <+47>: movq %rax, -0x48(%rbp)
0x100000b63 <+51>: callq *0x4a7(%rip) ; (void *)0x0000000100344640: objc_msgSend
0x100000b69 <+57>: movq 0x710(%rip), %rsi ; "init"
0x100000b70 <+64>: movq %rax, %rdi
0x100000b73 <+67>: callq *0x497(%rip) ; (void *)0x0000000100344640: objc_msgSend
0x100000b79 <+73>: movq %rax, -0x18(%rbp)
0x100000b7d <+77>: movq 0x714(%rip), %rax ; (void *)0x00000001000012d8: LGPerson
0x100000b84 <+84>: movq 0x6fd(%rip), %rsi ; "class"
0x100000b8b <+91>: movq %rax, %rdi
0x100000b8e <+94>: callq *0x47c(%rip) ; (void *)0x0000000100344640: objc_msgSend
0x100000b94 <+100>: movq %rax, -0x20(%rbp)
0x100000b98 <+104>: movq -0x18(%rbp), %rax
0x100000b9c <+108>: movq 0x6ed(%rip), %rsi ; "sayNB"
Debug can be found by compiling four LGPerson method called alloc, init, class, sayNB,
Which init, sayNB can see objc_msgSend. Continue to analyze
Find target directory, using the terminal Clang -rewrite-ObjC main.m , compiled main.cpp file, the main function of the control of both
// oc
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [[LGPerson alloc] init];;
[person sayNB];
}
return 0;
}
// c++
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
LGPerson *person = ((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)((LGPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("LGPerson"), sel_registerName("alloc")), sel_registerName("init"));;
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));
}
return 0;
}
- Comparative analysis can be found, we call [person sayNB] method, in C ++ were compiled into a objc_msgSend ((the above mentioned id) the Person, sel_registerName ( "sayNB")) .
- Can be known from the above two places at the bottom of the nature of the method is actually objc_msgSend send message, call the method is actually invoked objc_msgSend send a specific message to a specific object
- objc_msgSend (id, sel) id message recipient (Object) method number SEL
- The resulting key & mask obtained index, and then find the corresponding imp
- Send message function is to find a process, oc bottom package c is the real function is to find imp achieved by sending message
- If c function called directly, there will not process the message sent is called directly.
- // send message: objc_msgSemd
// object method - Person - SEL
// class methods - class - SEL
// Parent Class: objc_superMSgSend
control + in entering into the objc_msgSend method, in which it is found libobjc.A.dylib , then the next, we can be explored by Source Search
objc_msgSend analysis
In objc source in the global search objc_msgSend method, objc-msg-arm64.s find the file objc_msgSend entry
ENTRY _objc_msgSend
UNWIND _objc_msgSend, NoFrame
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
b.eq LReturnZero
#endif
// person - isa - 类
ldr p13, [x0] // p13 = isa
GetClassFromIsa_p16 p13 // p16 = class
LGetIsaDone:
CacheLookup NORMAL // calls imp or objc_msgSend_uncached
This place is written in assembler, is not very good read. Upon inquiry simple analysis
- cmp p0, # 0 // nil check and tagged pointer check mark null pointer, is sentenced empty
- Analyzing SUPPORT_TAGGED_POINTERS operation (support pointers mark)
- p13 is equivalent isa
- Followed by GetClassFromIsa_p16 obtain the current class class
- By CacheLookup to find first-cache.
- In fact, basically the same type and structure, is written in assembly language, to be the address of the current class offset 16 bytes (ISA superClass and each occupies 8 bytes), where to find the cache structure, and then find the buckets to find a caching method
- If not found, proceed CheckMiss
.macro CheckMiss
// miss if bucket->sel == 0
.if $0 == GETIMP
cbz p9, LGetImpMiss
.elseif $0 == NORMAL
cbz p9, __objc_msgSend_uncached
.elseif $0 == LOOKUP
cbz p9, __objc_msgLookup_uncached
.else
.abort oops
.endif
.endmacro
CheckMiss
Depending on the parameters passed, and returns the result
objc_msgSend type just passed to the NORMAL , so the next calls __objc_msgSend_uncached method, and only internal calls MethodTableLookup method and a callback method pointer
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
- Class structure. Isa first find by category, to find buckets cache of class, after all is not found in the cache, then the next class went to the original storage space, which is the class - bits - rw - ro - methodList the find method Definition.
- Compilation of messages sent is the same in MethodTableLookup method, first made a series of preparatory work, after calling the __class_lookupMethodAndLoadCache3 method, a method to find and load the cache,
- The following is a compilation of the end of the C / C ++
macro MethodTableLookup
// push frame
SignLR
stp fp, lr, [sp, #-16]!
mov fp, sp
// 一系列准备工作
// save parameter registers: x0..x8, q0..q7
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
stp q2, q3, [sp, #(2*16)]
stp q4, q5, [sp, #(4*16)]
stp q6, q7, [sp, #(6*16)]
stp x0, x1, [sp, #(8*16+0*8)]
stp x2, x3, [sp, #(8*16+2*8)]
stp x4, x5, [sp, #(8*16+4*8)]
stp x6, x7, [sp, #(8*16+6*8)]
str x8, [sp, #(8*16+8*8)]
// receiver and selector already in x0 and x1
mov x2, x16
// 前面都是准备工作 在一系列准备工作之后,进入到方法的查找以及缓存的加载
bl __class_lookupMethodAndLoadCache3
IMP _class_lookupMethodAndLoadCache3(id obj, SEL sel, Class cls)
{
return lookUpImpOrForward(cls, sel, obj,
YES/*initialize*/, NO/*cache*/, YES/*resolver*/);
}
Note: entering from assembly to C ++ stage approach that requires the compilation method to remove an underscore in order to search in the source code.
- After __class_lookupMethodAndLoadCache3 a bit confused I do not know what to do next, and this place just looked at the statistics.
- control + in entering objc_msgSend after method, looking down and found a __objc_msgSend_uncached method
- Entering __objc_msgSend_uncached interior, at the breakpoint, which is found a jump is located objc-runtime-new.mm C ++ method file
to sum up:
- objc_msgSend is written in assembler, mainly fast enough, and flexible enough (C language can not write a function to retain the unknown parameters and jump to an arbitrary function pointer)
- Dynamic identification can be compiled, faster and higher performance. Assembler machine more easily identified
- objc_msgSend first by fast path to find the cache, if you can not find the entered MethodTableLookup list of methods to find, by _class_lookupMethodAndLoadCache3 to find and cache
Spread
消息的发送
LGStudent *s = [LGStudent new];
[s sayCode];
// 方法调用底层编译
// 方法的本质: 消息 : 消息接受者 消息编号 ....参数 (消息体)
objc_msgSend(s, sel_registerName("sayCode"));
// 类方法编译底层
// id cls = [LGStudent class];
// void *pointA = &cls;
// [(__bridge id)pointA sayNB];
objc_msgSend(objc_getClass("LGStudent"), sel_registerName("sayNB"));
// 向父类发消息(对象方法)
struct objc_super lgSuper;
lgSuper.receiver = s;
lgSuper.super_class = [LGPerson class];
objc_msgSendSuper(&lgSuper, @selector(sayHello));
//向父类发消息(类方法)
struct objc_super myClassSuper;
myClassSuper.receiver = [s class];
myClassSuper.super_class = class_getSuperclass(object_getClass([s class]));// 元类
objc_msgSendSuper(&myClassSuper, sel_registerName("sayNB"));
The top message (class methods) to the parent
Call super_class parent class, and then look for reasons to go because metaclass
To metaclass class to go, go to the metaclass can not find the parent class metaclasses.
s -> s metaclass -> s metaclass of the parent class
Direct access to the parent class, then a message is sent directly
s parent -> s metaclass of the parent class