Principio subyacente de OC: investigación subyacente de 01-alloc

Este blog habla principalmente sobre el proceso de asignación de objetos OC, que se divide principalmente en dos pasos: 1: Cómo explorar el proceso de ejecución del método alloc 2: Analizar el proceso de ejecución de la función alloc

El siguiente es el diagrama de flujo de la asignación de objetos OC

1.png

1: Cómo explorar el flujo de ejecución del método alloc

Como se muestra en la figura anterior, se trata de algunos métodos llamados en el proceso de asignación de un objeto OC.

  1. Podemos establecer puntos de interrupción simbólicos directamente a través de puntos de interrupción simbólicos de xcode, combinados con xcode (presione y mantenga presionado el comando, haga clic en paso para ingresar)

2.png

imagen.png

imagen.png

Establecemos un punto de interrupción en la línea de código donde queremos ver la creación de instancias de un objeto (debido a que se creará una gran cantidad de objetos cuando se ejecuta el programa, para eliminar la interferencia, primero podemos desactivar el punto de interrupción simbólico y abrirlo cuando se alcanza el punto de interrupción. ) En este punto, podemos ver que objc_alloc es llamado internamente por alloc , y luego podemos establecer un punto de interrupción simbólico directamente. Intercepte la función objc_alloc para ver qué método se llama internamente, y así sucesivamente

2. Ver la cadena de llamadas a través de la depuración del desmontaje

imagen.png

Seleccione Mostrar siempre desensamblado , ubique rápidamente el punto de interrupción del símbolo combinado objc_alloc , mantenga presionado el comando, haga clic en paso a paso para entrar y paso a él.

imagen.png

imagen.png

3. Analizamos directamente el código fuente. Sabemos que objc_alloc es una función en libobjc.A.dylib a través de los puntos de interrupción simbólicos anteriores y la depuración de desensamblaje. Vamos a Apple Open Source para descargar el código fuente para compilar y depurar en un solo paso.

Dos: analizar el flujo de ejecución de la función alloc

imagen.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

imagen.png

imagen.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执行后面的流程

总结

El proceso de una asignación de clase personalizada: al llamar a objc_alloc->callAlloc , hay dos casos de caché y no caché en callAlloc. Cuando hay caché, pase _objc_rootAllocWithZone -> _class_createInstanceFromZone -> instanceSize -> calloc -> initInstanceIsa no hay caché, callAlloc El método alloc se llama de objc_msgSend , y _objc_rootAlloc -> callAlloc se ejecuta dentro del método alloc Los siguientes pasos son los mismos que los anteriores.

Supongo que te gusta

Origin juejin.im/post/7080419416387059719
Recomendado
Clasificación