OC对象(一)-- alloc和init底层到底在干嘛

OC对象(一)-- alloc和init底层到底在干嘛
OC对象(二)-- 内存对齐和calloc中的16字节对齐
OC对象(三)-- isa结构分析


本文使用的源码是objc4-787.1

init


看看init源码:

- (id)init {
    return _objc_rootInit(self);
}

id _objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}

init方法中调用了_objc_rootInit方法,最后返回的就是self。所以可以说init中什么都没有做。

用个demo验证一下:

DZObject *obj1 = [DZObject alloc];
DZObject *p1 = [obj1 init];
DZObject *p2 = [obj1 init];
    
NSLog(@"%@ - %@ - %@", obj1, p1, p2);
NSLog(@"%p - %p - %p", obj1, p1, p2);
NSLog(@"%p - %p - %p", &obj1, &p1, &p2);

说明:

  1. DZObject是自定义的类,继承自NSObject。
  2. alloc一个实例obj1
  3. 再分别定义实例p1和p2,通过obj1调用init方法
  4. 分别打印实例对象、指针、和指针地址

打印结果如图:



对象和指针都是相同的,指针地址不同。内存中结构如图:


init方法是苹果提供的工厂方法,对实例对象没有进行任何处理。开发者可以自定义init的实现,比如初始化一些属性数据的工作。

alloc


alloc流程图


图中粉色部分是alloc的核心部分,主要进行了三部操作

  1. cls->instanceSize:计算需要开辟的内存空间,内部实现是个算法,在最新源码的算法是16字节对齐。
  2. calloc:用计算好的size作为参数,调用calloc方法,开辟内存空间。
  3. obj->initInstanceIsa:初始化示例的isa
instanceSize实现(字节对齐算法)

函数方法调用流程图入下:


核心代码如下:

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}

计算过程示例:例如x=4,如下表:

16进制 2进制
x=4 0000 0100
+
15 0000 1111
=
19 0001 0011
&
~15 1111 0000
16 0001 0000

经过算法后,得到的结果都是16的倍数(可以使用大于16的数字算一遍)。这就是16字节对齐。

new


经常会用new方法直接示例对象,例如

DZObject *obj = [DZObject new];

看看new的底层源码实现:

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

通过源码可以看到,new做了alloc和init合并的事。==但是不建议直接使用new。如果一个自定义类中添加了initWithXXX:方法,方法中做了初始化的相关逻辑。那么直接调用new,只是调用了系统提供的init方法。==

NSObject alloc


当用NSObject alloc的时候,走的流程与上面的一样。其实alloc的真正调用的是objc_alloc方法,验证方式:在alloc下断点,打开汇编,运行:


可以看到汇编代码调用的是objc_alloc,接下来看看它的源码实现:

// Calls [cls alloc].
id
objc_alloc(Class cls)
{
    return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}

因此alloc的真正的流程图是这样的:


扩展 - 那为什么调用alloc方法时会先调用objc_alloc函数呢?


苹果使用LLVM编译工具,在编译期间对alloc方法进行了特殊处理,可以理解为是系统级别的hook方法,也就是将alloc方法和objc_alloc函数进行交换。
展示一下部分llvm源码中tryGenerateSpecializedMessageSend函数中的部分实现:

再来看看tryGenerateSpecializedMessageSend的调用地方:

大致的思路是,系统将alloc标记为特殊消息发送。一个类调用alloc的时候系统会去调用objc_alloc。在objc_alloc源码中通过objc_msgSend再次发送一次alloc消息(这次不会被系统标记成特殊消息,正常调用alloc方法)。

你可能感兴趣的:(OC对象(一)-- alloc和init底层到底在干嘛)