iOS alloc & init 探索

0x000 从哪里入手?

先看看main函数

0x001 初探?

  1. 为什么是 alloc init?
  2. alloc init 做了什么?

热身:先来打印一些信息:


屏幕快照 2019-12-20 下午7.03.52.png

热身疑问:
alloc 已经创建了对象,为什么还要有init?
alloc 怎么创建的?

alloc 也看不到源码,但是我们需要了解alloc 的源码实现?
如何快速探索出Object-C一个方法的底层所在实现

通过上述方法的探索,能知道 alloc 的底层实现就是libobjc.A.dylib 'objc_alloc'

0x002 源码初解:libobjc.A.dylib 'objc_alloc'

苹果开源源码objc/libsystem/dyld/coreFoundation(CF)
objc4-750源码 alloc 解析

证明 alloc确实是创建了对象

如何证明?
申请内存空间,有指针地址?

  1. 如果想证明上述所说,可以跑源码跟断点一步步查看。
    通过源码分析,我们能看出在alloc的底层实现里面,alloc确实有申请内存空间创建对象。
alloc 创建对象跟踪

结论:
calloc,申请一片空间大小
initInstanceIsa: 关联class(isa)

  1. 不想断点一步步,也可以在libobjc.A.dylib 'objc_alloc' 汇编处直接查看寄存器状态,来证明

需要进行定点分析,找主线
跳到断点到需要研究的地方:(确定研究对象)
通过简单的源码查看,我们可以依次加几个符号断点:

  • 加符号断点+alloc
  • 加符号断点+_objc_rootAlloc
  • 加符号断点+callAlloc
  • 加符号断点+class_createInstance

在汇编处查看寄存器状态:
x0在汇编里面是传递值的地方也是返回值得地方


截屏2019-12-24下午4.29.16.png

register read (读取寄存器)判断是否来到了我们需要的分析的方法
register read x0
刚进来x0还是TestPerson,没有返回地址

截屏2019-12-24下午4.32.23.png

结论:最后确实返回了地址,证明申请了内存空间,也就是创建了对象

0x003 源码详解:libobjc.A.dylib 'objc_alloc' (直接跑源码)

通过 0x2 读源码
可以知道alloc创建对象的核心:cls->instanceSizecallocinitInstanceIsa,现在来依次看看这几个方法

calloc开辟多少内存空间呢?

cls->instanceSize 计算对象需要的内存大小

计算size前


empty-object计算size前.png

内存对齐 [为什么要内存对齐]

内存对齐.png

计算size后


empty-object计算size后.png

calloc:申请内存空间 & initInstanceIsa:关联ISA

 obj = (id)calloc(1, size);
 if (!obj) return nil;
    obj->initInstanceIsa(cls, hasCxxDtor);

系统创建一块内存地址并且与类关联起来。(相当于建了房子,要有一个房本将房子与房主关联起来。)
initInstanceIsa 详解

内存空间里面有些什么?(房子里面住谁?)

属性

下面有一个这样的对象,来看看对象内存地址里面分别存着什么


截屏2019-12-24下午6.24.56.png

打印地址


截屏2019-12-24下午6.05.23.png

结论:po 地址,验证地址与属性关系,可以验证,确实这些属性都会存放在对象开辟的内存地址空间内。只是属性地址分布的地址段会按照内存对齐的原则以及做一些优化,存放,具体请看内存对齐原则.

补充:也可以查看全部地址信息

截屏2019-12-24下午6.09.18.png

地址段
截屏2019-12-24下午6.10.41.png

0x004 最后说说init

从热身的打印结果看,其实alloc后就已经有创建了对象实例,init什么也没做.

那init意义何在:
工厂设计模式:预留,方便子类自定义重写

0x005 new

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

alloc底层调用

id
  _objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

callAlloc

// Call [cls alloc] or [cls allocWithZone:nil], with appropriate 
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{....}

所以其实
[xxx new] == [[xxx alloc] init];

0x006 总结

  1. 定位 alloc 源码位置
  2. 粗略验证 alloc 创建对象
  3. alloc, 调用流程
    _objc_rootAlloc ——> callAlloc ——> class_createInstance ——> _class_createInstanceFromZone(cls->instanceSize;(id)calloc(1, size);obj->initInstanceIsa(cls, hasCxxDtor);)
  4. 分析内存大小计算逻辑
  5. init 和 new
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容