Objective-C 底层对象探究-上

目录

1. 背景

对iOS对象alloc方法进行了详细研究,目的是为了了解对象底层的本质、和对象在内存中的结构。如果你也有同样的兴趣?不要怀疑的阅读下去吧!~

2. 底层探索的三个方法

  • 通过符号断点:

    首先我们将断点打到 ZXPerson *p1 = [ZXPerson alloc];这段代码来已此作为我们探索的入口。
    开始编译运行之后我们来到断点,通过按住control点击Setp into之后,我们会进入到汇编页面。

    图片.png

    在汇编顶部显示的alloc_test`objc_alloc那么我们就清楚alloc之后是调用的 objc_alloc这个函数,紧接着我们进行对这个函数添加符号断点继续调试。
    图片.png
    图片.png

    图片.png

    图片.png

    点击continue继续跟踪,发现新添加的符号断点已经断住了,我们从汇编界面分析在调用objc_alloc之后,会调用一个叫做_objc_rootAllocWithZone的函数,最后会调用一个objc_msgSend的函数发送消息,而后就完成了alloc的流程。

  • 通过汇编:

    首先我们先通过Xcode设置开启汇编显示功能,Debug > DebugWorkFlow >Always show Disassembly 之后运行。

    图片.png
    这时候我们看到红框标示的那段语句,从后面的提示我们就可以得知,这段代码实际就是调用了objc_alloc的方法了。后续我们再根据objc_alloc增加符号断点即可。
    ps:这里分享点汇编的小知识 我们看到的bl这个指令是属于 ARM 架构的汇编语法,他的功能是跳转到 0x104182564 这个地址去执行相关程序,并且把他下一步执行的地址0x1041821fc保存到 lr寄存器(又可以叫做x30寄存器) 中以便在objc_alloc执行ret命令之后可以回来继续往下执行

  • 通过符号断点快速定位:

    最后一种方法最暴力,当断点执行到ZXPerson *p1 = [ZXPerson alloc];时,我们直接添加alloc的符号断点即可马上定位。
    那么,到此我们3中探索的方法就结束完了。

    图片.png

3. 如何进行源码调试

当我们知道探索方法以及入口之后,我们怎么能有效的进行代码跟踪呢?如果是下载源码进行静态分析显然让人觉得不是那么爽,如果可以做到就跟调试我们自己编写的程序一样那就太完美了吧。是否真的能实现呢?答案当时是可行的,下面我们就来搞起!

首先我们现需要去苹果开源网站去下载源码,根据我们上面探索的结果发现alloc的底层都是由objc来负责的,所以我们需要的就是objc4-818.2的源码。但是!当你兴冲冲的下载完毕打开项目并且编译时,你就发现根本编译不通过会有很多错误。怎么办?

  • 方案一:大家可以参考这个文章来进行处理解决。解决源码顺利编译方法步骤

  • 方案二(推荐):就是直接拿人家编译好的下载即可。最新macOS源码编译开源项目

    图片.png

    图片.png
    以上都准备好之后我们来打开项目,我们创建一个target选择命令行工具。
    图片.png
    创建完成之后,将我们之前的ZXPerson拷贝到target目录下面。
    图片.png
    图片.png
    ps:注意这里有一个坑点,就是在 Build Phases的 Compile Sources下把 main.m 文件夺挪到第一位来,要不可能不会触发断点,我不知道是不是我的Xcode(v12.5)问题,大家可以试一下
    main.m文件里编写ZXPerson初始化的代码,同时加上断点。
    图片.png
    最后,在新建的target的build Settings 中搜索runtime ,找到Enable Hardened Runtime选项,将其改为NO;然后继续在Build PhasesDependencies中引入objc库。
    图片.png
    图片.png
    接着激动人心的时刻来了,选则好target运行即可。当触发断点时点击Step into可以继续跟踪时我们就大功告成了!
    图片.png
    图片.png
    图片.png
    到这里咱们已经具备进行源码调试的能力了,下面就可以探索一下alloc的主线流程啦。
    ps:注意有一个调试技巧,每次想跟踪对象时,先把除 ZXPerson *p1 = [ZXPerson alloc]; 之外的断点关闭,等断点断在 ZXPerson *p1 = [ZXPerson alloc]; 时,再把相关的断点打开,否则会有其他对象触发断点,而就不是我们想追踪的 ZXPerson 对象了。

4. 编译器的优化(LLVM优化)

这部分内容我只想简单的描述一下,不想做过多的解释,因为这个知识点我们平时并不需要特别关注,只要理解原理即可。

  • 原理:
    当我们编写完Objective-C程序完成编译之后,最终都会以汇编形式进行执行,那么在这个编译过程中,编译器(LLVM)会对我们的代码进行优化处理,具体他会根据程序来缩减、删除、简化等方式进行处理,例如:我们定义了一个变量 NSString *str 但是并没有使用它,虽然这个变量是存在于我们的程序代码中的,但是最终编辑器会将这段代码进行删除,这个过程就是编译器的优化,大家只用理解这个概念即可。
  • 在Xcode中控制优化等级:
    Build Settings中搜索optimi回到看到一个Optimization Level选项后面就是可以调整优化级别例如:
    图片.png
    图片.png

    这时我们会发现,当处于Release时默认就是最快且最小模式,而在Debug模式下就是默认不优化的状态。
    图片.png

5. alloc的主线流程

  • 第一步:我们先来到了objc_alloc方法,通过名称我们大致可以猜到,通过[cls alloc]方式alloc对象时,都应该先走到这里。

    图片.png

  • 第二步:第二步:来的allAlloc方法,这个方法有几个分支我们先不用管,先把分支打上断点,通过断点我们发现这里直接走到最后return语句,这段话通过objc_msgSend方法cls类的alloc方法发送消息。

    图片.png

  • 第三步:我们来到alloc这个只是过渡方法直接无视继续往下。

    图片.png

  • 第四步:来到objc_rootAlloc方法,还是过渡方法直接无视继续往下。

    图片.png

  • 第五步:又来到了allAlloc方法,这次进入了objc_rootAllocWithZone方法。

    图片.png

  • 第七步:又是一个过渡方法直接无视。
    图片.png
  • 第五步:进入class_createInstanceFromZone方法,我们先通过这个方法返回值来分析,通过查看我们发现返回的是一个叫obj变量,在往上查找就看到了obj变量的初始化代码,我们可以整体的分析出来大致的逻辑,首先通过instanceSize来得到对象在内存所需的大小;然后对obj对象重新分配内存空间,这个obj对象可以理解成是一个空对象,它本身并没有说明含义,因为我们alloc的是ZXPerson类的对象,所以还需要将objZXPerson类建立绑定关系,而来联系这层关系的就是我们熟悉的Isa。后面hasCxxDtor是将C++的相关功能也赋值给这个obj。
    图片.png
    图片.png

    说了这么多我们一起来验证一下obj对象的变化,直接上图更直观。
    图片.png
    图片.png
    图片.png
    通过LLDB调试我们分别打印了obj在内存里面的变化。
  • 最后返回obj
    图片.png
  • 最后附上一个流程图:
    图片.png

6. 对象在内存中的结构

  • 一个类的实例在创建之后不添加任何代码的情况下,在内存中占用的大小是8字节,为什么是8字节呢?因为实例对象在内存结构中存放在第一位的是Isa指针,而指针的大小就是占用8字节。我们可以通过增加断点进行验证;
    图片.png
    如上图,我们可以再左侧实例对象中观察到Isa的指针地址为0x011d8001000080e9,然后我们利用LLDB在右侧输入x zxp(显示 zxp 指针的内存情况),等待打印出结果后我们就会看到,首个8字节的地址,因为iOS属于小端模式所以在读取内存时是从右往左读,我们可以p 0x011d8001000080e9打印一下看看是否会显示Isa的内容,结果出来之后并没有跟我预想的一样,原因是需要&上ISA_MASK,为什么需要&上ISA_MASKISA_MASK值是什么?带着这两个问题我们一起来寻找答案。
  • 我们从alloc流程中已经得知,与初始化Isa相关的事情都是在_class_createInstanceFromZone()函数中实现的,那么我直接来到改函数的initInstanceIsa()方法,然后跟进查看一下;
    图片.png
    跟进之后发现了叫initIsa()方法,继续前进。
    图片.png
    到这里我们看到了程序再给一个叫newisa的对象赋值,而这个对象的类型是isa_t,我们都知道Isa指向的是该对象的类信息,这里已经明显的有setClass()的方法,我们只需看看是否有getClass方法?该方法中是否有我们想找的东西。
    图片.png
    继续跟踪isa_t,果然发现了getClass方法,继续跟进查到了clsbits &= ISA_MASK通过上面的注释,大致猜到是MASK是一个掩码,目的是为了屏蔽除了类指针与签名之外的一些东西。那么我们再看一下ISA_MASK内容是什么?
    图片.png
    图片.png
    通过搜索我们找到了。我这里因为是非ram64架构的,所以匹配到了这个0x0000000ffffffff8ULL
    图片.png
    最后我们来验证一下!果然打印的是Isa指针指向的类
    图片.png
  • 刚才我们是在不添加任何代码的情况下,现在我们增加几个属性变量看一下内存的变化;然后我们这回用过x/4gx zxp方式对打印进行格式化(每隔4段以16进制的数据进行展示)结果如下:
    图片.png
    图片.png
    图片.png
    我们发现zxp对象第一个位置还是Isa,后面的数据分别存储了zxNamezxAgezxSexzxHieght,优化的部分不知道大家是否看出来了,zxAgezxSex因为是int类型(占4字节)与char类型(占1字节)所以共用了8字节的空间,这就是内存对齐(有关内存对齐的内容我会在下一篇中介绍)。下面我们分别来验证一下:
    图片.png
  • 结构示意图:
    图片.png

总结:

  • 我们知道了如何通过三种方式来探索底层代码;

  • 通过下载编译好的源码项目,使我们可以通过调试来进行探索。

  • LLVM是有优化策略的,可以在Xocde中可以手动修改。

  • alloc的主线流程

  • 对象在内存中的结构

    到此本篇内容以及结束!如果您喜欢的话可以赏个赞!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容