(二)alloc、init、new之间的关系

557 阅读3分钟

前言:OC面向对象编程的,我们常常使用 allocinitnew来把类实例化成对象,那么他们他们是如何工作的,以及他们之间有什么联系呢?

一.先看一段代码

NSObject *objc1 = [NSObject alloc];
NSObject *objc2 = [objc1 init];
NSObject *objc3 = [objc1 init];
NSLog(@"%@ - %p",objc1,&objc1);
NSLog(@"%@ - %p",objc2,&objc2);
NSLog(@"%@ - %p",objc3,&objc3);

输出:

InitViewController.m:(23): <NSObject: 0x600000a94be0> - 0x7ffee89358b8
InitViewController.m:(24): <NSObject: 0x600000a94be0> - 0x7ffee89358b0
InitViewController.m:(25): <NSObject: 0x600000a94be0> - 0x7ffee89358a8

由log可以看出objc1objc2objc3是由不同的指针指向同一块内存地址的 图示:

WechatIMG417.png 为什么会产生这样的结果呢?这还要从代码中找原因:

点击command + jump to alloc 发现跳到NSObjct.h中了

+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");

到这里就结束了,那我们要在哪里看alloc是如何实现的呢

二、3种断点方式

下面有三种打断点的方式,可以看见源码那那个库中

方法1:control + step into
objc_alloc: -> 
libobjc.A.dylib`objc_alloc: -> 
libobjc.A.dylib`_objc_rootAllocWithZone: -> 
libsystem_malloc.dylib`calloc: -> ...
方法2:符号断点

位置:Debug->BreakPoints->Create Symbolic Breakpoint

符号 alloc 会出现 libobjc.A.dylib +[NSObject alloc]:

方法3: 查看汇编

位置:Debug->Debug Workflow->Always show Disassembly

断点,然后进入汇编 control + step in 会进入libobjc.A.dylib objc_alloc:

以上的三种方法都指向了同一个库libobjc.A.dylib 接下来让我们分析一下个库

三、源码分析

objc源码地址

如何编译这个源码 在上一篇博客objc4-818.2源码 编译中写到

3.1.0 alloc实现原理

MMObject类什么都没有实现

#import <Foundation/Foundation.h>
#import "MMObject.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        MMObject *objc = [MMObject alloc];
    }
    return 0;
}
+ (id)alloc {
    return _objc_rootAlloc(self);
}
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {//没有实现 +allocWithZone
        return _objc_rootAllocWithZone(cls, nil);//走这里
    }
#endif

    // No shortcuts available.
    if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;

    size = cls->instanceSize(extraBytes);// 计算需要开辟的内存大小 参数为0
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {走这
        obj = (id)calloc(1, size);//申请内存
    }
    if (slowpath(!obj)) {
        if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if (!zone && fast) {//走这
        obj->initInstanceIsa(cls, hasCxxDtor);//将类与isa关联
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if (fastpath(!hasCxxCtor)) {//走这
        return obj;//返回这个对象
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}

这个是alloc的源码的过程 alloc -> _objc_rootAlloc -> callAlloc -> hasCustomAWZ() 没有实现+allocWithZone -> _objc_rootAllocWithZone -> _class_createInstanceFromZone 里面 申请内存calloc 将isa与类关联initInstanceIsa 返回 objc

3.1.1 MMObject类中实现了+allocWithZone
#import "MMObject.h"
@implementation MMObject

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static id instance = nil;
    @synchronized (self) {
        if (instance == nil) {
            instance = [super allocWithZone:zone];
        }
    }
    return instance;
}
@end

前面都是一样的,hasCustomAWZ()会跳过去

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if (slowpath(checkNil && !cls)) return nil;
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {//不包含自定义的 +allocWithZone
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif

    // No shortcuts available. 没有使用快速创造方式
    if (allocWithZone) {//走这里 因为实现了allocWithZone 
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}

WechatIMG454.png

3.2 init 的实现原理
MMObject *objc1 = [objc 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我们可以看见它什么都没有做,就返回了一个obj,那个这个有什么用呢,这是一种工厂设计模式,可以方便我们在初始化的时候可以做一些其他的操作。

3.2 new 的实现原理
MMObject *objc2 = [MMObject new];
+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}

这句代码可以看见new调用了callAlloc参数默认false``allocWithZone=false,并且默认调用了init的方法。

总结

alloc开辟了内存空间,并且将类关联了isa指针

init什么都没有做,返回了原理的类

new相当于固化了实例化方式调用了init[Class new]等价于[[Class alloc]init]