6, iOS bottom - A message (objc_msgeSend) Analysis

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;
}
  1. 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")) .
  2. 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
  3. objc_msgSend (id, sel) id message recipient (Object) method number SEL
  4. 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

  1. cmp p0, # 0 // nil check and tagged pointer check mark null pointer, is sentenced empty
  2. Analyzing SUPPORT_TAGGED_POINTERS operation (support pointers mark)
  3. p13 is equivalent isa
  4. Followed by   GetClassFromIsa_p16  obtain the current class class
  5. By CacheLookup to find first-cache.
  6.  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
  7. 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

 

 

 

 

 

 

 

 

 

 

Published 83 original articles · won praise 12 · views 180 000 +

Guess you like

Origin blog.csdn.net/shengdaVolleyball/article/details/104051671