NSThread的main方法内部做了什么?

Posted chaoguo1234

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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],执行完成后退出

以上是关于NSThread的main方法内部做了什么?的主要内容,如果未能解决你的问题,请参考以下文章

iOS —— 多线程NSThread

iOS并发编程对比总结,NSThread,NSOperation,GCD - iOS

iOS 多线程NSThread理解与场景示例

在main之前,IAR都做了什么

创建一个对象都在内存中做了什么事情

iOS多线程篇:NSThread