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位内存对齐
领取专属 10元无门槛券
私享最新 技术干货