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方法内部做了什么?的更多相关文章

  1. Java:关于main方法的10道面试题

    感觉假期过得好快,东西也丢得快. 假期吃喝玩乐之余也来温故一下Java知识,下面给大家整理了10道Java main方法的经典面试题,都来挑战一下自己的Java基础知识吧! 1.main方法是做什么用 ...

  2. main方法执行之前,做什么事

    1.我们知道程序的入口是main方法,那么在执行main方法之前,需要做些什么准备工作呢? 2.main方法执行之前,必须要把non-local static对象构造完成.static对象有:全局对象 ...

  3. 类(静态)变量和类(静态)static方法以及main方法、代码块,final方法的使用,单例设计模式

    类的加载:时间 1.创建对象实例(new 一个新对象时) 2.创建子类对象实例,父类也会被加载 3.使用类的静态成员时(静态属性,静态方法) 一.static 静态变量:类变量,静态属性(会被该类的所 ...

  4. 不包含适合于入口点的静态"Main"方法

    学习新建项目.此问题做为笔记. 错误 1 程序“admin.exe”不包含适合于入口点的静态“Main”方法 原因:原来创建项目的时候,用的是“空项目”,我以为这样就会生成类库,实际上,一开始准备运行 ...

  5. 07 java main方法

    1.问题:Java main方法为什么是  public static void main(String[] args)??? 序号 场景 编译 运行 解释 1 public修改为private pr ...

  6. Java中的static关键字解析(转自海子)__为什么main方法必须是static的,因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问。

    Java中的static关键字解析 static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键 ...

  7. [Java] JVM 在执行 main 方法前的行为

    JVM 执行一个 Java 程序时,先从某个指定的 Java 类的 main 方法开始执行代码,同时,传一个字符串数组作为 main 方法的参数.例如在 Unix 系统上,执行下面的命令 java T ...

  8. 【细说Java】揭开Java的main方法神秘的面纱

    大家都知道,main方法是Java应用程序的入口,其定义格式为: public static void main(String[] args) 可是为什么要这么定义呢?不这样定义可以么?main方法可 ...

  9. 【细说Java】揭开Java的main方法神秘的面纱(转)

    大家都知道,main方法是Java应用程序的入口,其定义格式为: public static void main(String[] args) 可是为什么要这么定义呢?不这样定义可以么?main方法可 ...

  10. public static void main(String[] args){} 关于Java main()方法

    是Java程序的入口方法,JVM在运行程序时,会首先查找main()方法. public是权限修饰符,表明任何类或对象都可以访问这个方法: static表明main()方法是一个静态方法,即方法中的代 ...

随机推荐

  1. 在idea/webstorm等terminal运行命令报错:Command rejected by the operating system没有权限【已解决】

    在idea/webstorm等编译器terminal窗口运行命令报错:Command rejected by the operating system没有权限[已解决] 1.修改terminal窗口 ...

  2. CTFshow pwn31 wp

    PWN31 使用checksec查看保护 发现除了canary剩下保护全开,那么就没有前面几个题目那么简单了,ida打开看见他给了我们main函数地址 虽然开了pie但是在他们之间的偏移是一定的,那么 ...

  3. std::thread 一:创建线程的三种方式

    前言: #include <thread> thread.join() // 阻塞 thread.detach() // 非阻塞 thread.joinable() // bool,判断线 ...

  4. HarmonyOS智能座舱体验是怎样炼成的?立即查看

    原文链接:https://mp.weixin.qq.com/s/AGY2hAeXngtRrZFk0FXe5g,点击链接查看更多技术内容: 随着AITO问界M5热卖以及M7.M5 EV版本的陆续交付,A ...

  5. go切片和指针切片

    转载请注明出处: 在Go语言中,切片(Slice)和指针的切片(即切片中每个元素都是指向某种数据类型的指针)是两个不同的概念,它们各自具有特定的用途和优势. 切片(Slice) 切片是对数组的一个连续 ...

  6. Python/Spring Cloud Alibaba开发--前端复习笔记(1)———— html5和css3.html基础

    Python/Spring Cloud Alibaba开发–前端复习笔记(1)---- html5和css3.html基础 1)概述和基本结构 超文本标记语言.超文本指超链接,标记指的是标签. 基本结 ...

  7. 重新点亮linux 命令树————selinux[二十六]

    简介 简单整理selinux. 正文 selinux 是安全增强软件. 以前是系统安全是用户权限配置(用户自主控制),但是害怕用户自己设置问题,故而增加了一个selinux,也就是强制访问控制. 一般 ...

  8. python flashtext字符串快速替换,自然语言处理加速

    在自然语言处理当中,经常对数据集进行一些数据字符的替换,表情的替换,以便在tokenizer的时候不被识别成[unk],造成信息的缺失 常规方法使用python自带的replace方法实现,但数据量很 ...

  9. 【笔记】rocketMQ了解

    记录rocketMQ 忘了从哪儿保存的图了 原图链接:https://www.jianshu.com/p/2838890f3284

  10. 第二課:Mirth培養興趣之旅 ——由定時刷庫接口編程講起

    1.准备工作 1.1 本机安装vs2019:(https://visualstudio.microsoft.com/zh-hans/) 1.2 本机安装win64的MariaDB 10.3.27版本数 ...