iOS对象的底层原理探索(上)

我们的类在创建一个对象时平时开发都是通过alloc init或者new来创建,那么我们就会想为什么会是alloc init,如果是这样创建呢


image.png

通过打印我么发现p, p1, p2三者都指向同一个地址,也就是说他们是同一个对象。可见,在 MyClass 使用 alloc 方法从系统中申请开辟内存空间后 init方法并没有对内存空间做任何的处理,地址指针的创建来自于 alloc方法。
细心的你一定注意到了,p, p1, p2都是相差了8个字节。 这是因为,指针占内存空间大小为8字节,p, p1, p2 都是从栈内存空间上申请的,且栈内存空间是连续的。同时,他们都指向了同一个内存地址。

那么alloc和init在底层分别做了什么事情呢

汇编查找

我们先通过汇编查看一下,首先在MyClass alloc 处打个断点,勾选Xcode -> Debug -> Debug Workflow -> Always Show Disassembly,运行


image.png

下符号断点“alloc”

image.png

接下来就来到此处
image.png

一个名为 libobjc.A.dylib 的库,至此,我们就应该要去找苹果开源的库,以寻找我们想要的答案。

源码分析

接下来我们通过源码分析,打开源码objc4-838.1(提取码: inb9) 查找一下,通过编译源码断点调试一路查找如下
alloc方法会调用到此处

image.png

接着是调用callAlloc


image.png

然后objc_msgSend查找alloc方法


image.png

image.png

接着是 调用 _objc_rootAlloc


image.png

第二次调用callAlloc


image.png
image.png

image.png

到此时我们发现callAlloc执行了两次第一次方法内部执行了最后一行objc_msgSend查找alloc方法,第二次执行了_objc_rootAllocWithZone,那么为什么会执行两次呢, 原因通过继续查找进去发现是因为fastpath(!cls->ISA()->hasCustomAWZ()) 这个第一次条件不成立才会执行objc_msgSend,这个判断是判断cls是否被初始化以及是否自定义了allocWithZone方法。

继续查找,接着执行了_class_createInstanceFromZone

image.png

到此时我们发现alloc方法创建对象是通过_class_createInstanceFromZone创建出来的,此时我们可以总结出alloc的底层执行流程如下图


image.png

init & new

接下来我们可以看init和new分别作了什么事情


image.png

image.png

从源码我们可以看到,init方法并没有做任何操作,而new方法其实本质上就是alloc + init,那么init方法没有做任何操作为什么还这样设计呢,其实苹果这样做的原因就是让我们可以重写init来初始化一些我们自己的操作,比如初始化UI,数据请求等等

编译器优化

在汇编里面我们并没有发现_class_createInstanceFromZone方法,这个是因为编译器对代码的优化,编译器的优化等级可以在targets->Build Setting->Optimization Level下修改


image.png

None的优化等级并不代表编译器不会优化,只是说None的优化等级没有那么高而已

alloc内存申请(对象的字节对齐方式)

接下来我们看一下被编译器优化掉的_class_createInstanceFromZone方法的内部做了什么操作。在方法内部我们看到有这样一句代码


image.png

image.png

点击进入方法内部发现有这样一句代码if (size < 16) size = 16; 也就是说通过allo或者new创建出来的对象最小的大小是16个字节,我们再看一下字节对齐算法
无缓存时


image.png

image.png

image.png

有缓存时
image.png

image.png

从上图的算法及WORD_MASK宏定义得知在我们iOS中64位的操作系统下创建对象开辟内存是以8个字节来进行对齐的,32位系统下是4字节对齐,如果有缓存时则以16字节对齐,如此那么我们如何知道我们创建的对象到底是多大呢,我们返回_class_createInstanceFromZone内查看发现 size = cls->instanceSize(extraBytes); 只是计算对象开辟需要的内存大小,系统实际为对象分配的内存大小依赖的是calloc函数


image.png

而calloc函数是以16字节对齐的,至此我们得出对象分配内存在计算对象需要的内存使用的是8字节对齐,实际分配内存使用的是16字节对齐。此时现在有一个疑问,对象的内存分配系统为什么要这样处理呢,这就涉及了对象的本质,等下我们再研究,继续往下阅读源码发现_class_createInstanceFromZone方法在calloc后又执行了以下代码
image.png

打印执行前后的obj
image.png

从打印结果我们可以看出obj->initInstanceIsa方法做了一个操作,把calloc返回的内存地址关联到响应的类。至此我们可以得出_class_createInstanceFromZone方法内部主要做了以下几个操作
image.png

对象的本质

首先我们创建一个mac app项目,在main文件下创建一个这样一个类


image.png

然后打开终端进入当前项目下输入clang -rewrite-objc main.m 指令,这样我们在文件夹下就得到了一个.cpp的文件,打开.cpp文件搜索MyClass我们可以看到

image.png

image.png

此时发现对象的本质是一个objc_object的结构体,其结构体内存储的是isa指针 + 成员变量的值
1、那么为什么要字节对齐是因为字节是内存的容量单位。但是,CPU在读取内存的时候,却不是以字节为单位来读取的,⽽是以“块”为单位读取的,所以⼤家也经常听到⼀块内存,“块”的⼤⼩也就是内存存取的⼒度。如果不对⻬的话,在我们频繁的存取内存的时候,CPU就需要花费⼤量的精⼒去分辨你要读取多少字节,这就会造成CPU的效率低下,如果想要CPU能够⾼效读取数据,那就需要找⼀个规范,这个规范就是字节对⻬。
2、为什么对象内部的成员变量是以8字节对⻬,系统实际分配的内存以16字节对⻬?
以空间换时间。苹果系统采取16字节对⻬,这样分配是因为OC的对象中,第⼀位叫isa指针,它是必然存在的,⽽且它就占了8位字节,就算对象中没有其他的属性了,也⼀定有⼀个isa,那对象就⾄少要占⽤8位字节。如果以8位字节对⻬的话,如果连续的两块内存都是没有属性的对象,那么它们的内存空间就会完全的挨在⼀起,是容易混乱的。以16字节为⼀块,这就保证了CPU在读取的时候,按照块读取就可以,效率更⾼,同时还不容易混乱。

结构体对齐方式

我们知道了对象的本质是一个结构体,对象的内存对齐方式我们知道了,那么结构体的内存对齐是什么规则呢,通过打印以下几个结构体大小得知


image.png

image.png

image.png

struct1、struct2、struct3的大小分别是24、16、48个字节。也就是说在结构体内,变量位置不同或变量类型不同都会对结构体的大小产生影响,上面的结果我们也可以通过下面的规则计算出来


image.png

image.png

首先struct1、struct2内部没有结构体作为成员,所以我们通过规则1和规则3计算如下


image.png

而struct3内部有struct1这个结构体作为成员变量,所以我们通过规则1、2、3计算如下


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

推荐阅读更多精彩内容