1.OC底层-alloc探索

292 阅读3分钟

三种调试方法

  • 1.符号断点
    • 1.打上断点 image.png
    • 2.按住control键 点击step into AE09591C5AABD526FC1EB623F04BB8FF.jpg
    • 3.即可查看底层调用方法 image.png
    • 4.由于无法继续stepinto查看objc_alloc 需要手动添加断点调试 12B62603B23D0D28D559DE8FA2C02C22.jpg 4626CD0DF581FBB687BBE4A8D112FB57.jpg
    • step into 即可找到底层源码 libobjc.A.dylib`objc_alloc:
    image.png
  • 2.查看汇编调试
    • 打开汇编调试,即可在断点处进入汇编查看 FFB6308706DF157FA318AF4C120B3F3D.jpg 18D1AA7301D53FD4B779117441D1A55C.jpg
    • 2.也可在汇编方法前打上断点,再step into
      7A6F8F7395624A108D61E1FF6B26CC7B.jpg
    129299B87FE89D117D432601AA8995C9.jpg
  • 3.当无法确定调用了什么方法时但是至少知道alloc image.png
    • 1.写上方法名打上断点 24116A583EAD70655214B124FC990D38.jpg

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 image.png
  • 5.将_objc_rootAlloc、callAlloc、_objc_rootAllocWithZone,下好符号断点,并打开汇编调试。查看得知先调用的时_objc_rootAllocWithZone再调用objc_msgSend 24116A583EAD70655214B124FC990D38.jpg
  • 6.再往下走就可以查看_objc_rootAllocWithZone

image.png

  • 7.源码查看 LGPerson类

image.png

  • 8.分配一块脏地址

image.png

  • 9.进入 obj = (id)calloc(1, size);
  • 10.进入下一步后 发现地址发生改变 image.png
  • 11.地址与类进行绑定 image.png
    • 打印发现绑定成功 image.png
    • hasCxxDtor 绑定方法和函数
  • 12.返回出去了这个对象 image.png

开辟内存流程

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

  • 计算对象内存大小 image.png

  • 缓存 image.png

  • May be unaligned depending on class's ivars.

  • 类的IVAR可能未对齐。

image.png

image.png

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

image.png

  • 得到的结果为 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 image.png

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

  • 内存对齐 16 字节

  • 通过isa 获取类 地址

  • isa & isa_mask image.png

整理流程

  • 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对代码逻辑没有丝毫影响,应该只是告诉编译器对代码优化,提升编译效率。

计算内存大小过程

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

  • 流程示意图

alloc创建流程.png