关于alloc的底层分析

2020-01-21  本文已影响0人  我要当个程序员

用到的软硬件
1、Macbook Air
2、macOS Mojave 10.14.6
3、Xcode 11.3.1
4、Object-C(编程语言)

那么首先第一步我们先打开我们的macbook电脑,然后运行我们的xcode,创建我们的第一个工程,这里我就取名为alloc_init_test(如图)


1.png
2.png
3.png

废话说了好多进入正题吧,首先找到我们的main.m文件在里面添加代码,然后在确定我们的研究对象,以NSObject的alloc来研究,所以创建一个类来继承NSObject,这里我以创建Person的NSObject为例(至于为什么用NSObject 而不是 其他什么NSArray ,ViewCongroller为例,我只能抱歉,因为发现很多问题)

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Person.h"

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);

        Person *p = [[Person alloc] init];
        NSLog(@"%@",p);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

按住我们的cmmad键,点击alloc再点击Jump to Definition,这时候会发现,这里虽然跳转到了,但是根本没办法读取到alloc这个函数是如何实现的。这个是因为苹果封装了alloc的实现函数,在一个包体里面。你只能看到alloc的在.h文件里面的定义。那么怎么办呢!!!
其实苹果在很久以前就开源了一部分的核心代码,那么我可以告诉大家我们这里的alloc的方法定义在objc4,那么大家问,为什么你会知道alloc的方法在objc4这个源码文件里面。很遗憾,我只能说,过程我也没有弄明白,所以我也不敢写出来。

首先
1、苹果开源源码地址:opensource.apple.com/tarballs/
2、给出别人已经编译好的iOS_objc4-756.2 最新源码编译调试,可以直接使用。

好这里我弄到了一份objc4-750的原生代码,并且配置好了可以直接运行我们的Person进行调试。也就是说在我们的原生代码里面,跑我们自己写的东西,可以看到原生里面实现的方法and过程。好像有点帅。。

在我们配置好的objc4-750代码里(ps:这里需要配置一个Target,不然进入不鸟断点哦,配置方式
我这里配置了一个Alloctest的Target,配置完成后会生成一个Alloctest的文件夹,里面有一个main.m的文件,好了,你懂得,加入我们的Person类文件,并且在main.m里面添加代码

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
        Person *p = [[Person alloc] init];
        NSLog(@"%@",p);
    }
    return 0;
}

然后在Person这里打入我们的断点。运行即可调试。

好,让我们一步一步的来追踪,
断点_push_01

+ (id)alloc {
    //好像没有什么太多解释的东西
    return _objc_rootAlloc(self);
}

断点_push_02

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

断点_push_03

callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;
//用于判断objective-c 版本,是不是2.0,目前我们使用的objective-c版本都是此版本。
#if __OBJC2__
    //如果该对象没有自己的allocWithZone方法需要实现,这里我不懂
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        //查看一下类是否能快速分配内存
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            //查看一下类是否有析构函数
            bool dtor = cls->hasCxxDtor();
           //分配内存,给obj对象
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            //如果分配失败,那么交给错误处理
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            //初始化obj的isa
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif

    // No shortcuts available.
    //如果allocWithZone 为true,则实现allocWithZone 方法
    if (allocWithZone) return [cls allocWithZone:nil];
    return [cls alloc];
}

这里有一个问题,就是关于这句话slowpath(checkNil && !cls),找了一堆的资料也不知所以然。跳进这个方法里面是一个宏定义

#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))

__builtin_expect这个指令是gcc引入的,作用是允许程序员将最有可能执行的分支告诉编译器。这个指令的写法为:__builtin_expect(EXP, N)。
意思是:EXP==N的概率很大。
好吧,这里我的理解只能是,提高编译效率。。。
这里参考了两篇文章
iOS——runtime(4):浅析对象的创建
iOS——runtime(5):allocWithZone剖析

好了 最后最后附上一张alloc的流程图。。。。

alloc流程图.png
上一篇 下一篇

猜你喜欢

热点阅读