OC underlying principle - 01-alloc underlying research

This blog mainly talks about the process of OC object alloc, which is mainly divided into two steps: 1: How to explore the execution process of the alloc method 2: Analyze the execution process of the alloc function

The following is the flow chart of the OC object alloc

1.png

1: How to explore the execution flow of the alloc method

As shown in the figure above, it is some methods called in the alloc process of an OC object.

  1. We can set symbolic breakpoints directly through xcode symbolic breakpoints, combined with xcode (press and hold command, click step into to enter)

2.png

image.png

image.png

We set a breakpoint on the line of code where an object instantiation is to be viewed (because a large number of objects will be created when the program is running, to remove interference, we can disable the symbolic breakpoint first, and open it when the breakpoint is reached. ) At this point, we can see that objc_alloc is called internally by alloc , and then we can set a symbolic breakpoint directly. Intercept the objc_alloc function to see what method is called internally, and so on

2. View the call chain through disassembly debugging

image.png

Select Always Show Disassembly , quickly locate the objc_alloc combined symbol breakpoint, hold down command, click step into to enter, and step into it.

image.png

image.png

3. We directly analyze the source code. Through the above symbolic breakpoints and disassembly debugging, we know that objc_alloc is a function in libobjc.A.dylib . We go to apple open source to download the source code to compile and single-step debugging.

Two: Analyze the execution flow of the alloc function

image.png

当实例化一个NSObject类的实例,反汇编+源码调试,会发现并没有执行NSObject的类方法alloc,而是直接执行的objc_alloc

1. alloc

+ (id)alloc {
    return _objc_rootAlloc(self);
}

复制代码

2. objc_alloc

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

复制代码

callAlloc

static ALWAYS_INLINE id
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));
}

复制代码

callAlloc方法中,会发现会进入if (fastpath(!cls->ISA()->hasCustomAWZ()))

inline Class
objc_object::ISA(bool authenticated)
{
    ASSERT(!isTaggedPointer());
    return isa.getDecodedClass(authenticated);
}

// class or superclass has default alloc/allocWithZone: implementation
// Note this is is stored in the metaclass.
#define FAST_CACHE_HAS_DEFAULT_AWZ    (1<<14)
bool hasCustomAWZ() const {
    return !cache.getBit(FAST_CACHE_HAS_DEFAULT_AWZ);
}

复制代码

由此可以看出,当调用callAlloc,先通过当前cls的ISA 返回一个Class对象,然后判断当前cache中是否有缓存,如果有,执行_objc_rootAllocWithZone,如果没有,执行objc_msgSend, 继续运行程序, 会发现NSObject在cache中已经有缓存,直接执行了_objc_rootAllocWithZone

_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

static ALWAYS_INLINE id
_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) {
        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_rootAllocWithZone -> _class_createInstanceFromZone 创建一个类的实例,分三步:

1.cls->instanceSize 计算对象所需要的的内存大小

2.cls->calloc 申请开辟内存

3.cls->initInstanceIsa 通过isa,将对象于类关联

然后直接返回obj

添加一下自定义类型调用alloc

image.png

image.png 会发现p1实例在alloc执行流程中,cache中没有缓存,使用runtime objc_msgSend函数调用了alloc方法,然后再调用 _objc_rootAlloc -> callAlloc -> _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize -> calloc -> initInstanceIsa 然后跳过p1, 会发现p2的alloc执行流程与上面的NSObject一样,就不会通过runtime的 objc_msgSend调用alloc方法,因为此时缓存中已经有LGPerson的缓存了,就直接 _objc_rootAllocWithZone执行后面的流程

总结

The process of a custom class alloc: By calling objc_alloc->callAlloc , there are two cases of cache and no cache in callAlloc. When there is cache, pass _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize -> calloc -> initInstanceIsa no cache, callAlloc The alloc method is called internally through objc_msgSend , and _objc_rootAlloc -> callAlloc is executed inside the alloc method. The following steps are the same as above.

Guess you like

Origin juejin.im/post/7080419416387059719