iOS-对象实例化alloc方法

写在最前:记录一个最近看到的一个问题,实例对象是alloc建立的仍是init建立的?写的很差,还请看到的人轻喷!sass

有一个Person类,声明一个age属性bash

Person *p = [Person alloc];
    p.age = 10;
    Person *p1 = [p init];
    Person *p2 = [p init];
复制代码

首先有两个疑问: Q1: p1和p2是相同的吗? Q2: p1.age和p2.age的值是多少?(0或者10) 咱们先来看一下结果Q1:函数

p1=<Person: 0x6000029bca80>
p2=<Person: 0x6000029bca80>
复制代码

经过其内存地址能够看到这两个对象是同样的(可是没存地址同样就足以证实这两个对象是同一个东西吗,这不必定,p1,p2在初始化的时候,初始化的内容可能不同)ui

结果Q2:spa

p1.age=10
p2.age=10
复制代码

经过这个结果好像能够看出:指针

Person *p1 = [p init];
Person *p2 = [p init];

等价于

Person *p1 = p;
Person *p2 = p;

哦,彷佛init方法没什么用啊?
复制代码

那咱们就看一下alloc方法干了些什么 经过objc的NSObject代码:调试

1.
+ (id)alloc {
       reeturn _objc_rootAlloc(self);
}
可知返回的这个self应该就是指当前的这个类对象
2.在进一步跟进去_objc_rootAlloc
id
_objc_rootAlloc(Class cls)
{
       return callAlloc(cls, false/*chechNil*/, true/*allocWithZone*/);
}

3.跟进去callAlloc

static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (checkNil && !cls) return nil;
    
    if (!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(); id obj = (id)calloc(1, cls->bits.fastInstanceSize()); if (slowpath(!obj)) return callBadAllocHandler(cls); 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; } } // No shortcuts available. if (allocWithZone) return [cls allocWithZone:nil]; return [cls alloc]; } 4.这里主要用到的代码就是id obj = class_createInstance(cls, 0);跟进去看到 id class_createInstance(Class cls, size_t extraBytes) { return _class_createInstanceFromZone(cls, extraBytes, nil); } 5.继续跟进去_class_createInstanceFromZone static id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) { if (!cls) return nil;// 首先是判断是否为空 assert(cls->isRealized());// 检查是否已经realize 了 // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();

    size_t size = cls->instanceSize(extraBytes); // 这一步是获取对象大小的
    if (outAllocatedSize) *outAllocatedSize = size;
    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size); // 分配空间
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);// 初始化
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;

        // Use raw pointer isa on the assumption that they might be 
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }
    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}
最主要的代码就是
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);

复制代码

事实上咱们看不到苹果的源码,下面经过汇编代码来看一下(真机调试),首先在Person *p = [Person alloc];处打个断点,当运行到该断点时,在Xcode菜单栏-->Debug-->DebugWorkflow-->Always Show Disassembly选中,code

屏幕快照1.png
能够看到当前断点走到了23行(;分号表示说明,分号后面是说明内容)咱们都知道,若是把[Person alloc]换成objc_msgSend的写法应该是objc_msgSend(Person,@selector(alloc)),咱们看到第26行,(这里的bl至关于函数跳转,这一行指令就至关于在调用函数)接着在26行处打断点,咱们经过寄存器来看一下,这个msgSend()的两个参数是什么?
屏幕快照 2.png
(调用函数的时候,函数的参数是放在x0--x7这8个寄存器中的,关于寄存器的内容,我也只是浅层的了解,很差意思,后续会有补充) 如今咱们能够知道26行的代码,其实就是在执行[Person alloc],下面咱们alloc方法里下个符号断点,
屏幕快照3.1.png
屏幕快照3.2.png
以后过掉第26行的代码,
屏幕快照4.png
能够看到断点来到了NSObject alloc(当前Person类并无重写alloc方法,确定会来到其父类的alloc),能够看到这句代码调用的是_objc_rootAlloc,重复上一步,咱们在下一个_objc_rootAlloc的符号断点,而后过掉alloc的断点,
屏幕快照5.png
能够看到rootAlloc第一次的函数调用时在第21行有函数跳转class_createInstance方法,一样的给class_createInstance添加一个符号断点,过掉_objc_rootAlloc的断点,[图片上传中...(屏幕快照 2019-04-25 下午10.41.03.png-dafb56-1556203291738-0)] 咱们能够看到在第44行的ret(ret就至关于return),由前面咱们知道调用函数的时候,函数的参数是放在x0--x7这8个寄存器中的,当函数调用完成以后,x0寄存器存放的是返回值,
屏幕快照 7.png
能够看出此时x0存放的实际上是Person对象的指针,接下来咱们在第44行ret处添加断点,并过掉class_createInstance的断点,咱们再来看一下寄存器x0存放的内容
屏幕快照8.png
而后po获得的这个地址,能够获得<Person: 0x1cc00d500>,此时返回的是一个person对象,此时此刻,一个完整的对象就建立完成了。因此说alloc才是真正建立实例对象的方法。
相关文章
相关标签/搜索