1.OC底层-alloc探索

发布于 2022年 04月 08日 01:54

三种调试方法

  • 1.符号断点

    • 1.打上断点

    • 2.按住control键 点击step into

    • 3.即可查看底层调用方法

    • 4.由于无法继续stepinto查看objc_alloc 需要手动添加断点调试

    • step into 即可找到底层源码 libobjc.A.dylib`objc_alloc:

  • 2.查看汇编调试

    • 打开汇编调试,即可在断点处进入汇编查看

    • 2.也可在汇编方法前打上断点,再step into

  • 3.当无法确定调用了什么方法时但是至少知道alloc

    • 1.写上方法名打上断点

alloc的源码分析

  • 源码怎么编译成功的什么的我就不太懂了给个链接

juejin.cn/post/697132… 大家可以看看这位怎么做的。

  • 1. LGPerson *p = [LGPerson alloc] ;
  • 2.+ (id)alloc {return _objc_rootAlloc(self);}
  • 3
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
  • 4.进入这里思考是先调用 _objc_rootAllocWithZone 还是 调用 objc_msgSend

  • 5.将_objc_rootAlloc、callAlloc、_objc_rootAllocWithZone,下好符号断点,并打开汇编调试。查看得知先调用的时_objc_rootAllocWithZone再调用objc_msgSend

  • 6.再往下走就可以查看_objc_rootAllocWithZone

  • 7.源码查看 LGPerson类

  • 8.分配一块脏地址

  • 9.进入 obj = (id)calloc(1, size);
  • 10.进入下一步后 发现地址发生改变

  • 11.地址与类进行绑定
    • 打印发现绑定成功
    • hasCxxDtor 绑定方法和函数
  • 12.返回出去了这个对象

开辟内存流程

  • 对象的内存大小取决于成员变量属性。
  • 计算对象内存大小

  • 缓存

  • May be unaligned depending on class's ivars.
  • 类的IVAR可能未对齐。

  • Class's ivar size rounded up to a pointer-size boundary.
  • 类的ivar大小向上舍入为指针大小边界。
  • 说明这里对类的成员变量做了 字节对齐

  • 得到的结果为 8 的整数倍 向上取整
- 假如 x = 8
- X+7 & ~7  -> 8 + 7 & ~ 7
- 8 :  0000 1000               
- 7 :  0000 0111
- ~7 : 1111 1000
- & 运算 相同为1 不同为0
- 15   0000 1111
- &    1111 1000
----------------
       0000 1000
       - 得到结果为 8
       
- 假如 X = 9       
- 9 :  0000 1001               
- 7 :  0000 0111
- ~7 : 1111 1000
- & 运算 相同为1 不同为0
- 16   0001 0000
- &    1111 1000
----------------
       0001 0000  
       - 得到结果为 16
       
我认为可以理解为 X / 8  = 商 --余数
- 当有余数时 为 (商+1)*8
- 没有余数时 为  商 * 8 

位运算 >> 3 右移 3位 即 / 1000 如 15 : 0000 1111 >> 3 = 0000 0001 111
      << 3 左移 3位 即 * 1000 如 15 : 0000 1111 << 3 = 0111 1000
      所以 15 右移 3位 后 再 左移 3 位 得到了  0000 1000 为 8 

  • 对象的内存大小由 isa (8字节) + 成员变量的大小
  • 同时这里规定了如果内存大小小于16则设置为16

  • 字节对齐 8 字节 64位 为 8字节

  • 内存对齐 16 字节

  • 通过isa 获取类 地址

  • isa & isa_mask

整理流程

  • 1._objc_rootAlloc 传入 类
+ (id)alloc {
    return _objc_rootAlloc(self);
}
  • 2.callAlloc 这里有个同时传入 false 和 true
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
  • 3._objc_rootAllocWithZone 出入了 cls
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__  //是否有可用的编译器优化
//slowpath 大概率为假
//fastpath 大概率为真
//提升编译效率。
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));
}
  • 4._class_createInstanceFromZone
_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);
}

  • 5.真正核心的地方
_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); //计算内存大小
    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) {
        //将这块可使用的地址isa与cls类相关联
        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);
}

//slowpath 大概率为假
//fastpath 大概率为真
//此处去掉slowpath和fastpath对代码逻辑没有丝毫影响,应该只是告诉编译器对代码优化,提升编译效率。

计算内存大小过程

  • 计算需要开辟多少的内存空间

  • 流程示意图

推荐文章