001:对象原理上-alloc流程->cllass_createInstanceFromZone 1:cls->instanceSize 2:calloc 3: obj->initInstanceIsa

目录

1:底层原理探索方法

2:对象地址和指针地址  *

3:苹果源码

4:alloc的流程      ***

5:alloc的作用      **

6:属性影响分配内存大小   *

7:alloc init new 方法的区别

问题

1:  p1、p2、p3对象和地址打印都一致, 为何&p打印不一致

alloc是真正开辟内存和绑定对象的,p1、p2、p3共用1个alloc,所以他们都是指向同一目的地址。但是他们本身也是对象,在init时传入他们自身id&p打印的是他们自身的地址。

通俗的说:

我有一个房子出售,A、B、C三个都是我员工,他们都领着客户来看我这套房子。但是他们三个虽然都是我公司员工,但工号(id)不一样。
如果客户问他们房子在哪(等同于%@%p打印),他们都会告诉我房子的具体位置(三人说的一定相同)。
如果顾客问他们是谁(等同于打印&p),他们就会各自回答A、B、C。

2:alloc流程

1:alloc

2:_objc_rootAlloc

3:callAlloc

4:_objc_rootAllocWithZone

5:_class_createInstanceFromZone

  5.1:cls->instanceSize:计算需要开辟的内存空间大小

  5.2:calloc申请内存,返回地址指针

  5.3:obj->initInstanceIsa:将 类 与 isa 关联

6:有个问题:为什么一开始我们调用的alloc 的方法,底层却调用的是objc_alloc

正文

一:底层原理探索方法

1:底层原理探索方法

2:汇编查看流程

1:直接断点调试,bt  打印出堆栈信息

2:符号断点

3:断点+汇编调试

4:源码

二:对象地址和指针地址

%@ 打印对象

 %p 打印对象地址 

&p 打印指针地址,&p1:取出指针地址

 

p1是对象地址,

&p1是指针地址。

备注

说明对象地址是同一个地址

指针地址&p,是三个不同的指针地址,用&p来取出指针地址。

*p是说明p是一个指针类型。

p是一个变量的名字。

三:苹果源码

1:苹果开源源码汇总:

https://opensource.apple.com

2:这个地址用的更直接 objc4

 https://opensource.apple.com/tarballs/

四:alloc的流程  

断点显示函数调用堆栈信息

1:alloc

2:_objc_rootAlloc

3:callAlloc

4:_objc_rootAllocWithZone

5:_class_createInstanceFromZone

  5.1:cls->instanceSize:计算需要开辟的内存空间大小

  5.2:calloc申请内存,返回地址指针

  5.3:obj->initInstanceIsa:将 类 与 isa 关联

1:  首先根据main函数中的LGPerson类的alloc方法进入alloc方法的源码实现(即源码分析开始)
//alloc源码分析-第一步
+ (id)alloc {
    return _objc_rootAlloc(self);
}

2:跳转至_objc_rootAlloc的源码实现

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

3: 跳转至callAlloc的源码实现

callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__ //有可用的编译器优化
    // checkNil 为false,!cls 也为false ,所以slowpath 为 false,假值判断不会走到if里面,即不会返回nil
    if (slowpath(checkNil && !cls)) return nil;
    
    //判断一个类是否有自定义的 +allocWithZone 实现,没有则走到if里面的实现
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        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)); }

参考:

fastpath slowpath

4:跳转至_objc_rootAllocWithZone的源码实现

id  _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)// alloc 源码 第四步
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    //zone 参数不再使用 类创建实例内存空间
    return _class_createInstanceFromZone(cls, 0, nil,  OBJECT_CONSTRUCT_CALL_BADALLOC);
}

5:跳转至_class_createInstanceFromZone的源码实现,这部分是alloc源码的核心操作,由下面的流程图及源码可知,该方法的实现主要分为三部分

5.1:cls->instanceSize:计算需要开辟的内存空间大小

5.2:calloc申请内存,返回地址指针

5.3:obj->initInstanceIsa:将 类 与 isa 关联

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)// alloc 源码 第五步
{
    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;

    //1:计算需要开辟的内存大小,传入的extraBytes 为 0
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

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

    if (!zone && fast) {
        //3:将 cls类 与 obj指针(即isa) 关联
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 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);
}
hasCxxCtor():当前class或者superclass是否有.cxx_construct 构造方法的实现。
hasCxxDtor():该对象是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑,,如果没有,则可以更快的释放对象。
canAllocNonpointer():表示是否对 isa 指针开启指针优化。0:纯isa指针;1:不⽌是类对象地址,isa 中还包含了类信息、对象的引⽤计数等。
zone:在iOS8之后,iOS就不再通过zone来申请内存空间了,所以zone传参为nil。

 ******强烈推荐参考 

1:类名

2:地址

3:类名和地址关联

五:alloc的作用      

1:cls->instanceSize

 先计算出需要的内存空间大小

2:calloc

向系统申请开辟内存,返回地址指针

3:obj->initInstanceIsa

关联到相应的类

alloc创建对象时,系统会先经过instanceSize方法计算需要申请多大的内存空间.
再在calloc方法里对申请的内存大小进行16字节对齐处理,然后按处理后的结果来给对象分配内存空间,并返回内存地址。
系统最终实际为对象分配的内存空间大小为16字节的整数倍,并且最少16字节,如果instanceSize方法里是按16字节对齐的,那实际分配的内存大小和申请的内存大小相同,如果是按8字节对齐,则不同。

六:alloc init new 方法的区别

6.1:init是工程方式初始化方法

+ (id)init {
    return (id)self;
}

- (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 方法中是返回的当前对象,也就是自己本身。所以我们可以了解到 init 其实三个工厂方法,可以交给子类去自定义重写该方法

七:new方法:

+ (id)new {
    return [callAlloc(self, false/*checkNil*/) init];
}
通过 new的源码可以看出,其实它也是调用了 [callAlloc init]方法;
但是我们推荐使用 [alloc init]方法,因为这样我们可以自定义 init方法,使我们的开发更加的灵活。

new方法是alloc和init方法的组合,但是自定义的初始化方法,new方法没有调用。

9: 有个问题:为什么一开始我们调用的alloc 的方法,底层却调用的是objc_alloc 

/// Allocate the given objc object.
///   call i8* \@objc_alloc(i8* %value)
llvm::Value *CodeGenFunction::EmitObjCAlloc(llvm::Value *value,
                                            llvm::Type *resultType) {
  return emitObjCValueOperation(*this, value, resultType,
                                CGM.getObjCEntrypoints().objc_alloc,
                                "objc_alloc");
}

我们通过汇编在llvm编译器里里面找到相关代码

由此得出编译器阶段系统给我做了处理

引用

1:从源码看objc对象是如何产生的

2:OC底层(2)- alloc、init、new源码分析

3:iOS-底层原理 02:alloc & init & new 源码分析

4:lldb常用命令与调试技巧

5:简书课程 

posted on 2020-11-11 18:53  风zk  阅读(198)  评论(0编辑  收藏  举报

导航