zoukankan      html  css  js  c++  java
  • OC 底层探索 01、如何探索底层 + alloc 做了什么

    本文介绍 如何探索 alloc 和 alloc 做了什么?

    objc 可编译源码 

    从最简单的代码开始:

        MyPerson *p1 = [MyPerson alloc];
        MyPerson *p2 = [p1 init];
        MyPerson *p3 = [p1 init];
        
        NSLog(@"%@ - %p - %p",p1,p1,&p1);// p1 - 对象 - 指针
        NSLog(@"%@ - %p - %p",p2,p2,&p2);
        NSLog(@"%@ - %p - %p",p3,p3,&p3);

    打印结果如下:

    p1/p2/p3:对象是同一个MyPerson,指针指的同一个对象,指针地址不同。且其指针地址相差 8 字节,为什么呢?

    alloc 做了什么?我们点击 alloc 无法查看实现,只能停留在 NSObject.h 中?

    一、如何查找实现ku?

    查找实现库

    1、符号断点:

    run: --> [NSObject alloc] --> libobjc.A.dylib

    2、普通断点

    按住:control + step into -->

    添加符号断点:objc_alloc --> libobjc.A.dylib

    3、汇编查看

    debug->debug workflow -> always show disassembly

    运行:

    control + step into -->  如下图 --> 添加 符号断点 objc_alloc --> libobjc.A.dylib

    objc 源码下载 - 地址:最新版本 objc4-781

    二、进入源码 -- 流程分析

    打开下载的源码文件,由 C C++ 汇编共同编写。

    1、源码分析

    1.1)alloc 入口:

     

    我们通过点进去,发现:

    _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone / objc_msgSend 2者走谁?

    callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
    {
    #if __OBJC2__
        if (slowpath(checkNil && !cls)) return nil;
        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));
    }

    可通过:回到 demo,添加符号断点:_objc_rootAlloc / callAlloc / _objc_rootAllocWithZone --> 运行

    跟随断点,可知 alloc 走 _objc_rootAllocWithZone:

     

    1.2)开辟空间 _objc_rootAllocWithZone()

    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);
    }

    _class_createInstanceFromZone() --> 源码中 3 个方法(标红位置)

    _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;
        // 1: 算出要开辟多少内存
        size = cls->instanceSize(extraBytes);
        if (outAllocatedSize) *outAllocatedSize = size;
    
        id obj;
        if (zone) {// zone 已经废弃不走它 直接走 else
            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;
        }
    
        // 3: 指向内存的地址指针 和 cls 关联 起来
        if (!zone && fast) {
            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);
    }

    思考,是否可以直接运行源码走进方法进行调试呢?

    --> objc 源码编译调试 配置方法

    配置成功后,run。

    2、实际运行分析

    2.1)开辟多少内存空间 instanceSize()

    align16() --> size + 0 - 8 = 8; // size = 16

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

    字节对齐 (针对对象)-- 最新的是16字节对齐,苹果之前的对齐方式是8字节对齐。16字节更加安全,预留空间更多,不易产生野指针等。

    每个对象都有继承自NSObject 的 isa,一个指针8字节。

    2.2)申请开辟内存空间 calloc()  -->  2.3)内存指针和 类 cls 绑定 initInstanceIsa()

    三、init 和 new

    init:

     

    构造方法(工厂),用来给我们自定义开发 - 重写。

    new: --> alloc 的 callAlloc --> [MyPerson new];  ==》相当于 [[MyPerson alloc] init];

    但,我们 init 重写的一些方法,在new是无法实现的。 <-- 不同之处

    以上。

    tip:slowpath / fastpath 是什么? -- 编译器优化

    例如一些中间过程编译直接优化掉,节省时间,优化性能

    如上图,release 是 Feastest,Smallest, 发包苹果也会帮我们进行优化。 

    运行:

    我们将 debug 也改成 fastest,smallest,再次运行,可看到中间编译过程已被优化掉:

  • 相关阅读:
    Linq的一些常见Demo
    有一名员工发现日历已经7天没有翻了,于是他连着翻了7页,7天的总和刚好是138,问这一天是几号?
    20块钱,1块钱1瓶,两个空瓶子可以换一瓶,问最多可以喝几瓶?
    【转】Java编程之字符集问题研究
    Reset / Validate Buffer
    Article Master Data Deviation
    STAD Parameters
    Linux11.2 MySQL常用命令
    Linux11.1 设置更改Mysql的root密码及连接mysql
    Linux5.10 告警系统
  • 原文地址:https://www.cnblogs.com/zhangzhang-y/p/13621926.html
Copyright © 2011-2022 走看看