首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

iOS 底层-alloc与init

1、 什么是runtime

runtime 是C、C++、汇编实现的一套API,目的是为 OC增加运行时功能

2、 关于alloc与init到底在底层做了什么

看以下打印信息

打印结果为:

结果很奇怪,三个对象的地址都完全一样。既然很奇怪,那就 来看看源码,直接在xcode中是看不到源码的,我们需要在opensource中去下载涉及到的源码 ,在MacOS中的最新版本(当前是10.14.5)

opensource_macos.png

点开之后 搜索objc 得到2个搜索结果 下载objc4-xxxx(后面的代表版本号,会随着版本的更新而变化)

objc4.png

这里有一个问题,在opensource中,有如此多的源码,如何知道 要下载objc的这份源码呢,这里提供2种方式

1、汇编:

将断点打在 这一行

菜单栏 -> Debug -> Debug workflow -> Always show disassembly 勾选这个选项(也就是开启汇编模式)

程序运行起来,进入断点的时候出现这样的代码:(不同版本的Xcode,内容会稍有区别)

汇编模式(xcode 10.3)

汇编模式 Xcode 11.0

在这里我们大致可以猜一下 明显的双引号中间的红色文字,应该表示的意思就是方法名,当然在这里,这些可以不去理会。

进入objc_msgSend内部

点击 框起来的那个按钮,(control 不松开),进去之后 会出现很精简的汇编内容

control 进入之后

这个时候 需要添加符号断点(可能很多人只添加过全局断点,还不知道符号断点是啥)

添加符号断点

写上当前调用的方法名(关键词) 比如 ‘alloc’ ‘init’ ‘setimage:’等

为符号断点写上方法名

符号断点添加成功之后(可能会小卡一下)让断点直接走(下一曲图标的那个)就出现了我们需要的内容

需要的结果

有了这个结果,就可以去[opensource(https://opensource.apple.com/)搜索源码了。

2、偶然发现,不知道是否正确

进入API的系统声明,就是按住 ->,

箭头所指即为所需要的结果,拿这个信息去opensource搜索

偶然发现

2.1 alloc的底层实现

打开源码文件,搜索 (系统的方法 一般都是在方法最后加一个空格,然后才跟上大括号)进去之后 查看具体的实现,其经过了2次调用 在callAlloc中得到了返回实际的对象。

alloc

_objc_rootAllc

callAlloc

2.2 init的底层实现

同样的方法, 搜索 ,进去之后 只调用了一个方法 在 rootInit方法中 只做了一件事情, 就是 retrun obj。

init

objc_rootInit

到了这里 就知道了 为何上面 alloc之后 2次init 打印的地址都是一样的了。

2.3 new的底层实现

既然都已经进来了,那我们就来验证另外一个问题 ,我们经常说 new方法 其实就相当于是 alloc init,那么我们进入源码看看他们之间究竟是否有差别,

同样的方法, 搜索 进入之后 查看实现 里面只有一次方法的实现

new 方法的实现

在new方法里面就是调用alloc的实现(callAlloc)后 进行了init操作,由此可见,[Class new]完全等价于[[Class alloc] init]

问题来了

上面说,init没有任何操作, 由此引出另外一个问题:既然init没有做任何操作,为何还要有init这个步骤?

要想回答这个问题,先想想,init是在什么时候会用到?

重写方法的时候!在初始化一个类的时候,如果这个类的参数需要默认的值, 一般我们都会选择在 init方法中初始化这个值(比如封装一个倒计时按钮,一般都会在init中初始化 time = 60)。

父类不实现,交给子类根据需求去实现,

3 LLVM(编译器)

1、 补充 sieof()知识

sizeof() 是获取类型的大小

结构体的sizeof() 长度 用 的方式 是一个很好理解和计算的方法

举例1---简单型结构体:

举例2---复合型结构体:

如果使用 sizeof(obj) 得到的实际只是 指针的大小 也就是 4/8

想要知道实际上obj的内存,继续下面的问题讲解

2、 malloc_size() ——sizeof() —— class_getInstanceSize()

定义这样一个对象

注意和sizeof()的区别

问题:创建一个JEObject

对象内存占多少?

系统为其分配了多少内存

这里涉及到对象的本质,

NSobject的本质就是一个包含一个指针(这个指针是指向结构体的指针)的结构体

可以通过一个命令将OC代码转成C

cd 到待转换文件的路径

//命令解释:最后的 JEObject.m 是待转换文件的文件名, out.cpp 是输出文件的文件名

// 最后得到一个 out.cpp 的文件

在out.cpp中可以搜索JEObject_IMPL就能看到其具体结构

有3个函数是获取大小的,但是具体表示的意义不一样

Returns size of given ptr

系统为其分配的内存大小

获取对象所占内存大小

这是一个运算符 而不是方法,在编译的时候就是一个确定的数据

Returns the size of instances of a class

获取类的实例对象的成员变量所占用的大小

alloc的关键步骤:

alloc关键步骤

其中 instanceSize()方法的代码

内存对齐的算法:

3、类所占内存的大小

对于类所占用的内存要分情况来看,上面提到过,类的本质是结构体,那么类所占用的内存大小 就是这个结构体的内存大小,编译之后具体是怎样的结构体,要看具体是用什么方式声明的属性

->、 内部类的方式

对于这种方式,其转换为C代码之后(前面有提到OC转C的命令)变量的顺序不变,在最前面加上了isa指针,所以用的方法来计算这个JEObject的 为32 为32

2、用@property的方式声明属性

对于这种方式,其转换为C代码之后(前面有提到OC转C的命令)变量的顺序发生了变化(最前面的加上isa指针之后, 、),所以用的方法来计算这个JEObject的 为24 为32

具体转换的结果如下:

总结:

与 区别

文字理解:

malloc_size() 系统创建时 系统为对象分配了多少内存,

class_getInstanceSize() 对象实际利用了多少内存

代码层次的理解:

malloc_size () 可以认为是在 class_getInstanceSize() 之后 进行了一次16位内存对齐

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20191226A03EWJ00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券