NSThread的main方法内部做了什么?
NSThread当调用start方法的时候,start方法就会调用main方法。那么这个main方法内部做了什么呢?下面是汇编码:
1 ;Foundation`-[NSThread main]: 2 -> 0x7fff2594fa69 <+0>: push rbp 3 0x7fff2594fa6a <+1>: mov rbp, rsp 4 0x7fff2594fa6d <+4>: mov rax, qword ptr [rdi + 0x8] 5 0x7fff2594fa71 <+8>: mov rsi, qword ptr [rax + 0x20] 6 0x7fff2594fa75 <+12>: test rsi, rsi 7 0x7fff2594fa78 <+15>: je 0x7fff2594fa8e ; <+37> 8 0x7fff2594fa7a <+17>: mov rdi, qword ptr [rax + 0x18] 9 0x7fff2594fa7e <+21>: test rdi, rdi 10 0x7fff2594fa81 <+24>: je 0x7fff2594fa8e ; <+37> 11 0x7fff2594fa83 <+26>: mov rdx, qword ptr [rax + 0x28] 12 0x7fff2594fa87 <+30>: pop rbp 13 0x7fff2594fa88 <+31>: jmp qword ptr [rip + 0x5b02d3c2] ; (void *)0x00007fff50ba4400: objc_msgSend 14 0x7fff2594fa8e <+37>: pop rbp 15 0x7fff2594fa8f <+38>: ret
因为rdi寄存器存放的就是self,我们可以知道第4行的汇编其实就是从当前NSThread对象偏移8字节,将此处地址指向的值传给rax寄存器,其实就是获取NSThread对象内部的一个实例变量值。那么NSThread对象内部偏移8字节的地方是什么实例变量呢?我们使用下面的方法获取NSThread对象内部的实例变量:
1 (lldb) po [0x600002bbe600 _ivarDescription] 2 <NSThread: 0x600002bbe600>: 3 in NSThread: 4 _private (id): <_NSThreadData: 0x600000fe0a00> 5 _bytes (unsigned char[44]): Value not representable, [44C] 6 in NSObject: 7 isa (Class): NSThread (isa, 0x7fff87b504c8)
从上面的输出可以看到,NSThread对象内部的实例变量就3个,偏移8字节就是偏移了一个isa指针的长度,那么此时的实例变量就是上面输出第4行的_NSThreadData对象,也就是说rax寄存器此时是_NSThreadData对象的地址。
继续看main的汇编码第5行,该行汇编码将_NSThreadData对象偏移32字节处的实例变量值赋给了rsi寄存器。同样,我们查看_NSThreadData对象偏移32字节是什么实例变量:
1 po [0x600000fe0a00 _ivarDescription] 2 <_NSThreadData: 0x600000fe0a00>: 3 in _NSThreadData: 4 dict (id): <__NSDictionaryM: 0x600003edaa80> 5 name (id): @"com.apple.uikit.eventfetch-thread" 6 target (id): <UIEventFetcher: 0x6000001fc000> 7 selector (SEL): threadMain 8 argument (id): nil 9 seqNum (int): 2 10 qstate (unsigned char): Value not representable, C 11 qos (char): 33 12 cancel (unsigned char): Value not representable, C 13 status (unsigned char): Value not representable, C 14 performQ (id): nil 15 performD (NSMutableDictionary*): nil 16 attr (struct _opaque_pthread_attr_t): { 17 __sig (long): 1414022209 18 __opaque (char[56]): Value not representable, [56c] 19 } 20 tid (struct _opaque_pthread_t*): 0x600000fe0a88 -> 0x700004e45000 21 pri (double): 0.5 22 defpri (double): 0.5 23 in NSObject: 24 isa (Class): _NSThreadData (isa, 0x7fff87b504a0)
通过上面的输出,我们发现偏移32字节处是selector实例变量(计算偏移时不要忘了isa指针),也就是说现在rsi寄存器里面是selector的值。
main函数汇编第6行检测selector是否为空,为空就跳转专<+37>处,也就是汇编码第14行,此时main函数清除栈之后就会退出。如果selector有值,那么就会将_NSThreadData对象偏移24字节处的实例变量传给rdi寄存器(第8行)。对照上面的输出,就会发现rdi寄存器的值应该是target实例变量值。第9行汇编码会检测target是否为空,为空函数也会直接退出。如果不为空,就会将_NSThreadData对象偏移40字节处的实例变量传给rdx寄存器,此时rdx寄存器存储的是argument实例变量的值。有了target,有了selector,有了argument,汇编码第13行就调用objc_msgSend这个方法,调用[target selector:argument],执行完成后,main函数退出。
总结:
1 如果创建NSThread时不指定target或者selector,那么main函数就会直接推出;
2 如果都指定了,main函数会调用[target selector:argument],执行完成后退出