【iOS】Categoría, Extensión y objetos relacionados

Clasificación de categorías

CategoryEs una forma de Classextensión más concisa que la herencia y puede agregar métodos dinámicamente a clases existentes sin crear subclases.

  • Puedes agregarlo a cualquier clase existente en el proyecto.Category
  • Incluso se puede agregar a bibliotecas del sistema/bibliotecas de código cerrado que solo exponen clases en archivos de declaración Category( .mlas clases en archivos no se pueden ver)
  • CategoryPuede agregar métodos de instancia, métodos de clase y atributos a través de
    • Nota: Los atributos se pueden agregar a través de y Categoryse declararán los métodos , pero los desarrolladores deben implementar los métodos ellos mismos ( use objetos asociados para implementar atributos)settergettersettergetter
    • Al Classagregar atributos, los métodos se generarán e implementarán setterde forma predeterminada.getter
  • La clasificación también puede hacer frameworkpúblicos los métodos privados.
    • Por ejemplo, si tenemos una clase con un método privado A que no puede ser llamado por el mundo exterior, pero escribo un método para esta clase y declaro un método categoryen ella (también llamado A, solo se declara, no se implementa), ahora llamo importa esto categoryDe hecho, en este momento se llamará al método privado A. Hacemos público el método privado mediante clasificación.
  • Puede Categoryvolver a implementar Classmétodos que ya existen en
  • Puede Categoryvolver a implementar Categorymétodos que ya existen/implementados en otros
    [En iOS, el orden de llamada del método del objeto de instancia/objeto de clase depende estrictamente del orden de compilación del archivo de código fuente. El orden de compilación se puede ver viendo Xcode>Build Phases>Compile Sources:
    Insertar descripción de la imagen aquí
    • Las clases y cada categoría declaran e implementan sus propios métodos: no se anula ninguna implementación de método y las categorías solo amplían la funcionalidad de la clase.
    • La clase y cada categoría tienen declaraciones y métodos implementados con el mismo nombre: La implementación del método existente se sobrescribe (en realidad no se sobrescribe, pero la dirección del método se mueve más tarde. El sistema encontrará el método con el mismo nombre que es anteriormente en la dirección de memoria para implementar la llamada)
      • Prioridad de implementación del método de clasificación> clase original
      • La cobertura en cada categoría depende estrictamente del orden de compilación de los archivos de código fuente:
        • El método compilado primero se agregará primero a la lista de métodos y se "pondrá primero en la pila".
        • El método postcompilado se agregará a la lista de métodos "post-stack"
        • Cuando el sistema llama a la implementación de un método, envía mensajes en la parte inferior a través de objetos (objetos de instancia, objetos de clase, etc.) y API de método, y obtiene el puntero IMP de implementación de la implementación del método para encontrar la implementación específica del método. (En realidad, la implementación del método final obtenida es la implementación del método en el archivo de código fuente poscompilado)

Las ventajas de la presentación oficial son dos:

  • La implementación de la clase se puede separar en varios archivos diferentes.
    • Puede reducir el tamaño de archivos separados.
    • Se pueden organizar diferentes funciones en diferentes categorías
    • Varios desarrolladores pueden trabajar juntos para completar una clase.
    • Puede cargar las categorías deseadas a pedido, etc.
  • Declarar un método propietario

ExtensiónExtensión

La extensión ( Extension) puede entenderse como anónima.Category

Las declaraciones que se pueden usar para agregar atributos y métodos a una clase no funcionan. Puede agregarlos a otras clases o a la clase actual Subclass
a través de archivos. Al agregar a la clase actual , el compilador declarará los atributos agregados e implementará sus métodos mediante predeterminado. También puede agregarlos a través de archivos. La adición de clases requiere la implementación de los atributos y métodos agregados. Si los métodos existentes en la clase original u otras categorías se vuelven a implementar en la implementación, no afectará la ejecución del original. El método (porque no hay un archivo propio ni una compilación de archivos de implementación de código fuente) es más útil para dividir clases con estructuras complejas y exponer declaraciones con mayor claridad..mExtension
Extensionsetter&&getter
.hExtension
Extension
Extension.m
Extension

La esencia de la categoría

Estructura de categorías

typedef struct category_t *Category;
struct category_t {
    
    
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;  //实例方法
    struct method_list_t *classMethods; //类方法
    struct protocol_list_t *protocols;  //协议
    struct property_list_t *instanceProperties; //实例属性
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;   //类属性

    method_list_t *methodsForMeta(bool isMeta) {
    
    
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};

En la estructura clasificada, se pueden agregar a la clase métodos de objeto, métodos de clase, protocolos y atributos, pero no hay variables miembro.

Convertir clasificación a C++

Luego convertimos el archivo .m clasificado en un archivo C++ para averiguarlo:

Primero creamos una categoría:

#import "Car.h"
#import "protocolForCar.h"

NS_ASSUME_NONNULL_BEGIN

@interface Car (match)<CarProtocol>
@property (nonatomic, copy) NSString *carType;

- (void)matchPrint;
+ (void)matchClass;


@end

NS_ASSUME_NONNULL_END

Declaramos un método de instancia, un método de clase y un atributo en él.
La clasificación sigue un protocolo, y el protocolo también contiene un método de clase y un método de objeto.

#ifndef protocolForCar_h
#define protocolForCar_h

@protocol CarProtocol <NSObject>

- (void)protocolMethod;
+ (void)protocolClassMethod;

@end

Luego use: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Car+match.m -o test.cpppara convertir el archivo .m de esta categoría en un archivo C++:

//category结构体
struct _category_t {
    
    
	const char *name;
	struct _class_t *cls;
	const struct _method_list_t *instance_methods;
	const struct _method_list_t *class_methods;
	const struct _protocol_list_t *protocols;
	const struct _prop_list_t *properties;
};

//category结构体赋值
static struct _category_t _OBJC_$_CATEGORY_Car_$_match __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    
    
	"Car",
	0, // &OBJC_CLASS_$_Car,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Car_$_match,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Car_$_match,
	(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Car_$_match,
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Car_$_match,
};

//结构体数组
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
    
    
	&_OBJC_$_CATEGORY_Car_$_match,
};

Podemos ver los tres elementos de enfoque.

  • estructura de categorías
  • Declaración de asignación de estructura de categorías
  • matriz de estructura de categorías

Estructura de la lista de métodos de objetos

//本类对象方法的实现
static void _I_Car_match_matchPrint(Car * self, SEL _cmd) {
    
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_0);
}

//协议中对象方法的实现
static void _I_Car_match_protocolMethod(Car * self, SEL _cmd) {
    
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_2);
}

//对象方法列表结构体
static struct /*_method_list_t*/ {
    
    
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Car_$_match __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    
    
	sizeof(_objc_method),
	2,
	{
    
    {
    
    (struct objc_selector *)"matchPrint", "v16@0:8", (void *)_I_Car_match_matchPrint},
	{
    
    (struct objc_selector *)"protocolMethod", "v16@0:8", (void *)_I_Car_match_protocolMethod}}
};
  • - (void)matchPrinte - (void)protocolMethodimplementación del método
  • estructura del método del objeto estructura de la lista

Siempre que sea Categoryun método de objeto implementado (incluidos los métodos de objeto en el proxy). se agregará a la estructura de la lista de métodos del objeto _OBJC_$_CATEGORY_INSTANCE_METHODS_Car_$_match. Si es solo una definición pero no está implementada, no se agregará.

Estructura de la lista de métodos de clase

//本类类方法的实现
static void _C_Car_match_matchClass(Class self, SEL _cmd) {
    
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_1);
}

//协议中的类方法
static void _C_Car_match_protocolClassMethod(Class self, SEL _cmd) {
    
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_3);
}

//类方法列表结构体
static struct /*_method_list_t*/ {
    
    
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_CLASS_METHODS_Car_$_match __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    
    
	sizeof(_objc_method),
	2,
	{
    
    {
    
    (struct objc_selector *)"matchClass", "v16@0:8", (void *)_C_Car_match_matchClass},
	{
    
    (struct objc_selector *)"protocolClassMethod", "v16@0:8", (void *)_C_Car_match_protocolClassMethod}}
};
  • + (void)matchClasse + (void)protocolClassMethodimplementación de métodos de clase
  • Estructura de la lista de métodos de clase

Siempre que sea Categoryun método de clase implementado (incluidos los métodos de clase en el proxy). _OBJC_$_CATEGORY_CLASS_METHODS_Car_$_matchse agregará a la estructura de lista de métodos de clase.

estructura de la lista de protocolos

//协议结构体
struct _protocol_t {
    
    
	void * isa;  // NULL
	const char *protocol_name;
	const struct _protocol_list_t * protocol_list; // super protocols
	const struct method_list_t *instance_methods;
	const struct method_list_t *class_methods;
	const struct method_list_t *optionalInstanceMethods;
	const struct method_list_t *optionalClassMethods;
	const struct _prop_list_t * properties;
	const unsigned int size;  // sizeof(struct _protocol_t)
	const unsigned int flags;  // = 0
	const char ** extendedMethodTypes;
};

//分类中添加的协议列表结构体
static struct /*_protocol_list_t*/ {
    
    
	long protocol_count;  // Note, this is 32/64 bit
	struct _protocol_t *super_protocols[1];
} _OBJC_PROTOCOL_REFS_CarProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    
    
	1,
	&_OBJC_PROTOCOL_NSObject
};

//协议列表 对象方法列表结构体
static struct /*_method_list_t*/ {
    
    
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_PROTOCOL_INSTANCE_METHODS_CarProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    
    
	sizeof(_objc_method),
	1,
	{
    
    {
    
    (struct objc_selector *)"protocolMethod", "v16@0:8", 0}}
};

//协议列表 类方法列表结构体
static struct /*_method_list_t*/ {
    
    
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_PROTOCOL_CLASS_METHODS_CarProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    
    
	sizeof(_objc_method),
	1,
	{
    
    {
    
    (struct objc_selector *)"protocolClassMethod", "v16@0:8", 0}}
};

//结构体赋值
struct _protocol_t _OBJC_PROTOCOL_CarProtocol __attribute__ ((used)) = {
    
    
	0,
	"CarProtocol",
	(const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_CarProtocol,
	(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_CarProtocol,
	(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_CarProtocol,
	0,
	0,
	0,
	sizeof(_protocol_t),
	0,
	(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_CarProtocol
};
struct _protocol_t *_OBJC_LABEL_PROTOCOL_$_CarProtocol = &_OBJC_PROTOCOL_CarProtocol;

Estructura de la lista de propiedades

//属性结构体
struct _prop_t {
    
    
	const char *name;
	const char *attributes;
};

//属性列表结构体
static struct /*_prop_list_t*/ {
    
    
	unsigned int entsize;  // sizeof(struct _prop_t)
	unsigned int count_of_properties;
	struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_Car_$_match __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    
    
	sizeof(_prop_t),
	1,
	{
    
    {
    
    "carType","T@\"NSString\",C,N"}}
};

Del código fuente de la estructura de la lista de atributos, podemos ver que solo se personagrega la estructura de la lista de atributos en la clasificación _OBJC_$_PROP_LIST_NSObject_$_testCategoryy no hay _ivar_list_tuna estructura de variables miembro. No hay set/getcontenido correspondiente relacionado con el método.
Esto también ilustra Categoryel hecho de que las variables miembro no se pueden agregar en

resumen de categoría

Incluye principalmente las siguientes partes:

  • _method_list_tEstructura de lista de métodos de tipo objeto
  • _method_list_tEstructura de lista de métodos de clase de tipo
  • _protocol_list_testructura de lista de protocolos de tipo
  • _prop_list_tTipo de estructura de lista de propiedades
  • _category_tLa estructura no contiene _ivar_list_ttipos, es decir, no contiene estructuras de variables miembro.

¿Qué hace la clasificación en tiempo de ejecución?

(Esta parte del contenido está relativamente abultada. Primero puede consultar el resumen de carga de categorías al final del artículo y luego combinar el resumen con el análisis del código fuente aquí para hacerlo más lógico).

Para comprender este problema, necesitamos saber cuándo se llama al método de clasificación.

_objc_initEsta función es runtimela función de inicialización, comencemos _objc_initdesde el principio:

_objc_calor

/***********************************************************************
* _objc_init
* Bootstrap initialization. Registers our image notifier with dyld.
* Called by libSystem BEFORE library initialization time
**********************************************************************/

void _objc_init(void)
{
    
    
    static bool initialized = false;
    if (initialized) return;
    initialized = true;
    
    // fixme defer initialization until an objc-using image is found?
    //环境变量
    environ_init();
    //绑定线程析构函数
    tls_init();
    //静态构造函数
    static_init();
    //runtime准备,创建2张表
    runtime_init();
    //异常初始化
    exception_init();
#if __OBJC2__
    //缓存
    cache_t::init();
#endif
    //macos专有
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

Entre ellos, encontramos algunos procesos de creación de inicialización, aquí nos centramos principalmente en runtime_init:

void runtime_init(void)
{
    
    
    //分类加载表
    objc::unattachedCategories.init(32);
    //类的加载表
    objc::allocatedClasses.init();
}

Puedes ver que hay una tabla de carga de categorías.

Luego regresamos _objc_inital recurso, map_imagesleemos el recurso ( imagesque representa el módulo de recursos), llegamos a map_images_nolockla función para buscar _read_imagesla función y _read_imagesbuscamos el código relacionado con la clasificación en la función:

_leer_imagenes

    // Discover categories. Only do this after the initial category
    // attachment has been done. For categories present at startup,
    // discovery is deferred until the first load_images call after
    // the call to _dyld_objc_notify_register completes. rdar://problem/53119145
	//发现类别。只有在初始类别之后才这样做
	//附件已完成。对于启动时出现的类别,
	//发现延迟到之后的第一个load_images调用
	//调用_dyld_objc_notify_register完成。rdar: / /问题/ 53119145
	//意思是非懒加载的分类走的是load_images
	
	//那么作为对应,懒加载的分类就走的是这里 _read_images中的操作
    //全局变量didInitialAttachCategories,执行load_images 的时候设置为YES
    //所以只有当执行过load_images的时候,这里才会遍历load_catagories_nolock去加载分类,而这里遍历的也是一些懒加载的类的分类。
    //这里的判断条件didInitialAttachCategories意思是是否进行完初始的分类添加(如果进行过的话,也就是非懒加载的分类以经添加了的话,就进去执行if分支中的内容)
    if (didInitialAttachCategories) {
    
    
        for (EACH_HEADER) {
    
    
            load_categories_nolock(hi);
        }
    }

    ts.log("IMAGE TIMES: discover categories");

cargar imágenes

void
load_images(const char *path __unused, const struct mach_header *mh)
{
    
    
    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {
    
    
        didInitialAttachCategories = true;
        // 加载所有的分类
        loadAllCategories();
    }

    // Return without taking locks if there are no +load methods here.
    //有load方法的话直接返回,没有的话才执行后面找load方法和调用load方法的代码
    //如果这里没有+load方法,返回时不带锁。
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
    
    
        // 找到load方法
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }
    
    // Call +load methods (without runtimeLock - re-entrant)
    //调用load方法
    call_load_methods();
}

Ponemos un load_imagesdiagrama de flujo para facilitar la comprensión de todo el proceso.
Insertar descripción de la imagen aquí

Un punto importante es que al obtener el loadmétodo de clasificación, primero obtenemos la lista de categorías de carga no diferida y luego llamamos realizeClassWithoutSwifta la clase principal de la clasificación para implementarla, lo cual es muy importante. Más adelante en el resumen de todo el proceso se mencionará que realizeClassWithoutSwiftel código fuente de la llamada es el siguiente:

//prepare_load_methods是load_images中获取主类和分类load方法时调用的函数
void prepare_load_methods(const headerType *mhdr)
{
    
    
    size_t count, i;

    runtimeLock.assertLocked();

    classref_t const *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
    
    
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
    
    
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        if (cls->isSwiftStable()) {
    
    
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        //此处调用realizeClassWithoutSwift实现了分类对应的主类
        realizeClassWithoutSwift(cls, nil);
        ASSERT(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

realizeClassWithoutSwiftEl código fuente utilizado para implementar el método de la clase principal es el siguiente:

static Class realizeClassWithoutSwift(Class cls, Class previously)
{
    
    
    runtimeLock.assertLocked();

    class_rw_t *rw; // 读写数据
    Class supercls; // 父类
    Class metacls;  // 元类

    if (!cls) return nil; // 如果为空,返回nil
    if (cls->isRealized()) return cls;  // 如果已经实现,直接返回
    ASSERT(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?

    auto ro = (const class_ro_t *)cls->data(); // 读取类的数据
    auto isMeta = ro->flags & RO_META; // 是否是元类
    if (ro->flags & RO_FUTURE) {
    
     // rw已经有值的话走这里
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro();
        ASSERT(!isMeta);
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
    
     // 正常的类走这里
        // Normal class. Allocate writeable class data.
        rw = objc::zalloc<class_rw_t>(); // 开辟rw
        rw->set_ro(ro); // 把cls的数据ro赋值给rw
        rw->flags = RW_REALIZED|RW_REALIZING|isMeta; // 更新flags
        cls->setData(rw); // 再把rw设置为cls的data数据
    }

#if FAST_CACHE_META
    if (isMeta) cls->cache.setBit(FAST_CACHE_META);
#endif

    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
    
    
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // Realize superclass and metaclass, if they aren't already.
    //实现超类和元类(如果尚未实现)。
    // This needs to be done after RW_REALIZED is set above, for root classes.
    //对于根类,需要在上面设置了RW_REALIZED之后执行此操作。
    // This needs to be done after class index is chosen, for root metaclasses.
    //对于根元类,需要在选择类索引之后执行此操作。
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    // 递归调用 realizeClassWithoutSwift ,实现父类和元类
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);

#if SUPPORT_NONPOINTER_ISA
    if (isMeta) {
    
     // 如果是元类,对isa处理
        // Metaclasses do not need any features from non pointer ISA
        // This allows for a faspath for classes in objc_retain/objc_release.
        cls->setInstancesRequireRawIsa();
    } else {
    
     // 不是元类,也是对isa处理
        // Disable non-pointer isa for some classes and/or platforms.
        // Set instancesRequireRawIsa.
        bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
        bool rawIsaIsInherited = false;
        static bool hackedDispatch = false;

        if (DisableNonpointerIsa) {
    
    
            // Non-pointer isa disabled by environment or app SDK version
            instancesRequireRawIsa = true;
        }
        else if (!hackedDispatch  &&  0 == strcmp(ro->name, "OS_object"))
        {
    
    
            // hack for libdispatch et al - isa also acts as vtable pointer
            hackedDispatch = true;
            instancesRequireRawIsa = true;
        }
        else if (supercls  &&  supercls->superclass  &&
                 supercls->instancesRequireRawIsa())
        {
    
    
            // This is also propagated by addSubclass()
            // but nonpointer isa setup needs it earlier.
            // Special case: instancesRequireRawIsa does not propagate
            // from root class to root metaclass
            instancesRequireRawIsa = true;
            rawIsaIsInherited = true;
        }

        if (instancesRequireRawIsa) {
    
    
            cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
        }
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    // 确定继承链,赋值父类和元类
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
     // 协调实例变量的偏移量/布局。
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    // 经过上一步,再次协调属性对齐后,设置实例大小
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    // 赋值一些 ro 中的 flags标识位 到 rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
    
    
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
    
    
            cls->setHasCxxCtor();
        }
    }
    
    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    // 从ro或父类传播关联的对象禁止标志。
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
    
    
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    // 添加当前类到父类的子类列表中,如果没有父类,设置自己就是根类
    if (supercls) {
    
    
        addSubclass(supercls, cls);
    } else {
    
    
        addRootClass(cls);
    }

    // Attach categories
    // 附加分类
    methodizeClass(cls, previously);

    return cls;
}

Puedes ver que finalmente se ejecuta una función methodizeClasspara agregar categorías a la clase principal, methodizeClassel código fuente es el siguiente:

//methodizeClass函数用于附加分类
static void methodizeClass(Class cls, Class previously)
{
    
    
    runtimeLock.assertLocked();

    bool isMeta = cls->isMetaClass();
    auto rw = cls->data();
    auto ro = rw->ro(); // 读取ro数据
    auto rwe = rw->ext(); // 读取ext,赋值给rwe

    // Methodizing for the first time
    if (PrintConnecting) {
    
    
        _objc_inform("CLASS: methodizing class '%s' %s", 
                     cls->nameForLogging(), isMeta ? "(meta)" : "");
    }

    // Install methods and properties that the class implements itself.
    method_list_t *list = ro->baseMethods(); // 获取ro中的方法列表
    if (list) {
    
    
        // 对方法列表list重新排序
        prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
        if (rwe) rwe->methods.attachLists(&list, 1); // 如果有rwe,添加方法列表list到rwe的methodsList
    }

    property_list_t *proplist = ro->baseProperties;
    if (rwe && proplist) {
    
    
        rwe->properties.attachLists(&proplist, 1);
    }

    protocol_list_t *protolist = ro->baseProtocols;
    if (rwe && protolist) {
    
    
        rwe->protocols.attachLists(&protolist, 1);
    }

    // Root classes get bonus method implementations if they don't have 
    // them already. These apply before category replacements.
    if (cls->isRootMetaclass()) {
    
    
        // root metaclass 根元类添加initialize方法
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
    }

    // Attach categories. 附加分类
    if (previously) {
    
    
        if (isMeta) {
    
    
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_METACLASS);
        } else {
    
    
            // When a class relocates, categories with class methods
            // may be registered on the class itself rather than on
            // the metaclass. Tell attachToClass to look for those.
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

#if DEBUG
    // Debug: sanity-check all SELs; log method list contents
    for (const auto& meth : rw->methods()) {
    
    
        if (PrintConnecting) {
    
    
            _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(meth.name));
        }
        ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name); 
    }
#endif
}

Y descubrimos que la función load_imagesefectivamente se llamó loadAllCategories, y luego echemos un vistazo loadAllCategoriesa la implementación:

cargar todas las categorías

static void loadAllCategories() {
    
    
    mutex_locker_t lock(runtimeLock);

    for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
    
    
        //调用load_categories_nolock来加载分类
        load_categories_nolock(hi);
    }
}

A continuación, veamos load_categories_nolockla implementación de la función (este método se llamará en ambos procesos y el contenido de ejecución es ligeramente diferente) load_images:map_images

cargar_categorías_nolock

static void load_categories_nolock(header_info *hi) {
    
    
    bool hasClassProperties = hi->info()->hasCategoryClassProperties();

    size_t count;
    auto processCatlist = [&](category_t * const *catlist) {
    
    
        for (unsigned i = 0; i < count; i++) {
    
    
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);
            locstamped_category_t lc{
    
    cat, hi};

            if (!cls) {
    
    
                // Category's target class is missing (probably weak-linked).
                // Ignore the category.
                if (PrintConnecting) {
    
    
                    _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                 "missing weak-linked target class",
                                 cat->name, cat);
                }
                continue;
            }

            // Process this category.
            if (cls->isStubClass()) {
    
    
                // Stub classes are never realized. Stub classes
                // don't know their metaclass until they're
                // initialized, so we have to add categories with
                // class methods or properties to the stub itself.
                // methodizeClass() will find them and add them to
                // the metaclass as appropriate.
                if (cat->instanceMethods ||
                    cat->protocols ||
                    cat->instanceProperties ||
                    cat->classMethods ||
                    cat->protocols ||
                    (hasClassProperties && cat->_classProperties))
                {
    
    
                    objc::unattachedCategories.addForClass(lc, cls);
                }
            } else {
    
    
                // First, register the category with its target class.
                // Then, rebuild the class's method lists (etc) if
                // the class is realized.
                //首先,将类别注册到它的目标类。
				//如果那个类已经实现就重建它的方法列表
                if (cat->instanceMethods ||  cat->protocols
                    ||  cat->instanceProperties)
                {
    
    
                    if (cls->isRealized()) {
    
    
                    	//一般是map_images中调用load_categories_nolock函数时cls都会实现的,所以会走这个方法去将分类中的内容粘贴到主类中,但是现版本中map_images的注释中说到将分类添加主类的操作延迟到了第一次load_images执行时
                        attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                    //而如果是load_images中调用的load_categories_nolock函数的话,一般cls都没实现,就会走下面的else里的方法,将分类添加到unattachedCategories(未向主类粘贴内容的分类表)表中
                    } else {
    
    
                        //这个表就是分类表
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                }

                if (cat->classMethods  ||  cat->protocols
                    ||  (hasClassProperties && cat->_classProperties))
                {
    
    
                    if (cls->ISA()->isRealized()) {
    
    
                    	//一般是map_images中调用load_categories_nolock函数时元类也都会实现的,所以会走这个方法去将分类中的内容粘贴到主元类中
                        attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                    //而如果是load_images中调用的load_categories_nolock函数的话,一般其元类都没实现,就会走下面的else里的方法,将分类添加到unattachedCategories(未向主元类类粘贴内容的分类表)表中
                    } else {
    
    
                    // runtime_init的时候创建,第一部分有讲到
                        objc::unattachedCategories.addForClass(lc, cls->ISA());
                    }
                }
            }
        }
    };

    processCatlist(hi->catlist(&count));
    processCatlist(hi->catlist2(&count));
}
  • Obtener categorylistalist
  • Recorrer category listcada uno de loscategory
  • Obtenga categoryla clase principal correspondiente cls. Si no cls, omita ( continue) y continúe obteniendo la siguiente
    (cuando la clase principal correspondiente a la clasificación es un objeto de clase). Si tiene una clase principal correspondiente y tiene métodos de instancia, protocolos y atributos, luego llamar objc::unattachedCategories.addForClass( load_imagesCuando la ejecución llega a este punto, objc::unattachedCategories.addForClassse llama para registrar la categoría en su clase principal correspondiente para facilitar la reconstrucción posterior de la lista de métodos de la clase. Si la map_imagesejecución llega a este punto, se llamará attachCategories. El La razón por la que las ramas llamantes son diferentes en estos dos casos es que en load_imagesel momento de la llamada no se ha implementado la clase principal correspondiente a la clasificación, esas clases principales solo se implementan cuando load_imagesposteriormente se obtiene el método de obtención de la clasificación . Como resultado, la clase principal correspondiente a la clasificación ha sido implementada cuando la ejecución llega allí, por lo que la rama if es diferente).loadmap_images
  • (Cuando la clase principal correspondiente a la clasificación es un objeto de metaclase) Si tiene una clase principal correspondiente y tiene métodos y protocolos de clase, entonces llame (al ejecutar objc::unattachedCategories.addForClassaquí load_images) objc::unattachedCategories.addForClasspara registrar la clasificación en su clase principal correspondiente. Es conveniente para reconstruir la lista de métodos de la clase más tarde. Si se map_imagesejecuta aquí, se llamará attachCategories)

Definitivamente se preguntará aquí por qué load_imagesla clase se agrega primero a la clase unattachedCategoriesy luego la clase principal de implementación se agrega al contenido de la clase. map_imagesLa última llamada de read_imagesla clase también es la clase se agrega primero a la clase unattachedCategoriesy luego la implementación principal La clase se agrega al contenido de la clase. Clase, de hecho map_images, se puede entender en los comentarios anteriores sobre el código que maneja la clasificación. En nuestro load_images, agregar categorías a la clase principal maneja categorías de carga no diferidas, mientras que en el medio, read_imagesagregar categorías a la clase principal maneja la carga diferida. Las categorías cargadas, y son todas iguales, primero agregue las categorías unattachedCategoriespara asociar con la clase principal y almacene las categorías en la memoria, y luego espere hasta que realizeClassWithoutSwiftse llame a la función más tarde para implementar (inicializar ) la clase principal y luego llamar al methodizeClasscontenido de clasificación específico implementado.Agregar a la clase principal

En resumen, hay dos rutas para cargar categorías, una es la ruta para procesar categorías de carga no diferida y la otra es la ruta para load_imagesprocesar categorías de carga diferida .map_imagesread_images

Todo el map_imagesproceso es aproximadamente como se muestra a continuación:

Por favor agregue la descripción de la imagen.

Curiosamente, el paso 10 anterior, inicializar la clase de carga diferida, en realidad llama a lo que dijimos anteriormente realizeClassWithoutSwift.

Además, la diferencia entre clases con carga diferida y clases sin carga diferida es: si la clase actual implementa el loadmétodo. Si el método está implementado, loades una clase sin carga diferida, y viceversa. Además, la La carga de datos de la clase con carga diferida se pospone hasta que se recibe por primera vez. La carga comienza solo cuando se envía un mensaje, y las clases sin carga diferida cargan los datos map_imagesde todas las clases durante la ejecución.

Luego volvemos al negocio y volvemos a load_categories_nolockla función. Todo el proceso de la función es en realidad agregar todas las categorías a la tabla runtime_initinicializada o agregar directamente el contenido de la categoría a la clase principal. Tenga en cuenta que la tabla aquí significa que el contenido de la categoría no se agrega a la clase principal. Pegue las tablas clasificadas en la categoría principal, indicando que necesita realizar operaciones más tarde para pegar en la categoría principal.unattachedCategoriesattachCategoriesunattachedCategoriesattachCategories

Entonces echemos un vistazo a unattachedCategoriesla tabla:

class UnattachedCategories : public ExplicitInitDenseMap<Class, category_list>
{
    
    
public:
	//将分类和主类关联起来
    void addForClass(locstamped_category_t lc, Class cls)
    {
    
    
        runtimeLock.assertLocked();

        if (slowpath(PrintConnecting)) {
    
    
            _objc_inform("CLASS: found category %c%s(%s)",
                         cls->isMetaClassMaybeUnrealized() ? '+' : '-',
                         cls->nameForLogging(), lc.cat->name);
        }

        auto result = get().try_emplace(cls, lc);
        if (!result.second) {
    
    
            result.first->second.append(lc);
        }
    }
	//这个是向本类粘贴分类内容的方法
    void attachToClass(Class cls, Class previously, int flags)
    {
    
    
        runtimeLock.assertLocked();
        ASSERT((flags & ATTACH_CLASS) ||
               (flags & ATTACH_METACLASS) ||
               (flags & ATTACH_CLASS_AND_METACLASS));

        auto &map = get();
        auto it = map.find(previously);

        if (it != map.end()) {
    
    
            category_list &list = it->second;
            if (flags & ATTACH_CLASS_AND_METACLASS) {
    
    
                int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
                //可以看到调用了attachCategories加载分类
                attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
                attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
            } else {
    
    
                attachCategories(cls, list.array(), list.count(), flags);
            }
            map.erase(it);
        }
    }

    void eraseCategoryForClass(category_t *cat, Class cls)
    {
    
    
        runtimeLock.assertLocked();

        auto &map = get();
        auto it = map.find(cls);
        if (it != map.end()) {
    
    
            category_list &list = it->second;
            list.erase(cat);
            if (list.count() == 0) {
    
    
                map.erase(it);
            }
        }
    }

    void eraseClass(Class cls)
    {
    
    
        runtimeLock.assertLocked();

        get().erase(cls);
    }
};

Uno de ellos addForClasses en realidad cargar la clasificación en la memoria, encontramos un try_emplacemétodo en él, el código es el siguiente:

  template <typename... Ts>
  std::pair<iterator, bool> try_emplace(const KeyT &Key, Ts &&... Args) {
    
    
    BucketT *TheBucket;
    if (LookupBucketFor(Key, TheBucket))
      return std::make_pair(
               makeIterator(TheBucket, getBucketsEnd(), true),
               false); // Already in map.

    // Otherwise, insert the new element.
    TheBucket = InsertIntoBucket(TheBucket, Key, std::forward<Ts>(Args)...);
    return std::make_pair(
             makeIterator(TheBucket, getBucketsEnd(), true),
             true);
  }

Esto crea una estructura de depósito (esta es una estructura de par clave-valor) y almacena el contenido en ella. Combinado con la llamada a la función anterior, sabemos que esget().try_emplace(cls, lc) para almacenamiento .clskeylcvalue

attachCategoriesA continuación, echemos un vistazo al código fuente de la función que agrega contenido de la categoría a la clase principal :

// Attach method lists and properties and protocols from categories to a class.
// Assumes the categories in cats are all loaded and sorted by load order, 
// oldest categories first.
//将方法列表、属性和协议从类别附加到一个类。
//假设猫的类别都是加载的,并按加载顺序排序,
//最古老的类别先开始。
static void
attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
                 int flags)
{
    
    
    if (slowpath(PrintReplacedMethods)) {
    
    
        printReplacements(cls, cats_list, cats_count);
    }
    if (slowpath(PrintConnecting)) {
    
    
        _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
                     cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
                     cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
    }

    /*
     * Only a few classes have more than 64 categories during launch.
     * This uses a little stack, and avoids malloc.
     *
     * Categories must be added in the proper order, which is back
     * to front. To do that with the chunking, we iterate cats_list
     * from front to back, build up the local buffers backwards,
     * and call attachLists on the chunks. attachLists prepends the
     * lists, so the final result is in the expected order.
     */*
	*只有少数类在启动时拥有超过64个类别。
	*这使用了一个小堆栈,并避免了malloc。
	*
	*类别必须以正确的顺序添加,这是回来
	*前面。为了使用分块实现这一点,我们需要迭代cats_list
	*从前面到后面,向后建立本地缓冲区,
	并在区块上调用attachLists。attachLists突出显示的
	*列表,因此最终结果按照预期的顺序。
	* /
	//创建方法列表、属性列表、协议列表,用来存储分类的方法、属性、协议
    constexpr uint32_t ATTACH_BUFSIZ = 64;
    method_list_t   *mlists[ATTACH_BUFSIZ];
    property_list_t *proplists[ATTACH_BUFSIZ];
    protocol_list_t *protolists[ATTACH_BUFSIZ];

    uint32_t mcount = 0;// 记录方法的数量
    uint32_t propcount = 0;// 记录属性的数量
    uint32_t protocount = 0;// 记录协议的数量
    bool fromBundle = NO;// 记录是否是从 bundle 中取的
    bool isMeta = (flags & ATTACH_METACLASS);
    //取出当前类 cls 的 class_rwe_t 数据
    auto rwe = cls->data()->extAllocIfNeeded();
	
	//遍历分类
    for (uint32_t i = 0; i < cats_count; i++) {
    
    
        auto& entry = cats_list[i];
		
		// 取出分类中的方法列表。如果是元类,取得的是类方法列表;否则取得的是对象方法列表
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
    
    
            if (mcount == ATTACH_BUFSIZ) {
    
    
                prepareMethodLists(cls, mlists, mcount, NO, fromBundle, __func__);
                rwe->methods.attachLists(mlists, mcount);
                mcount = 0;
            }
            mlists[ATTACH_BUFSIZ - ++mcount] = mlist;// 将方法列表放入 mlists 方法列表数组中
            fromBundle |= entry.hi->isBundle();// 分类的头部信息中存储了是否是 bundle,将其记住
        }

		// 取出分类中的属性列表,如果是元类,取得的是 nil
        property_list_t *proplist =
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
    
    
            if (propcount == ATTACH_BUFSIZ) {
    
    
                rwe->properties.attachLists(proplists, propcount);
                propcount = 0;
            }
            proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
        }
		
		// 取出分类中遵循的协议列表
        protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
        if (protolist) {
    
    
            if (protocount == ATTACH_BUFSIZ) {
    
    
                rwe->protocols.attachLists(protolists, protocount);
                protocount = 0;
            }
            protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
        }
    }

    if (mcount > 0) {
    
    
    	// 存储方法、属性、协议数组到 rwe 中
    	// 准备方法列表 mlists 中的方法【为什么需要准备方法列表这一步?】
        prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount,
                           NO, fromBundle, __func__);
        // 将新方法列表添加到 rwe 中的方法列表中
        rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
        if (flags & ATTACH_EXISTING) {
    
    
        	// 清除 cls 的缓存列表
            flushCaches(cls, __func__, [](Class c){
    
    
                // constant caches have been dealt with in prepareMethodLists
                // if the class still is constant here, it's fine to keep
                return !c->cache.isConstantOptimizedCache();
            });
        }
    }
	
	// 将新属性列表添加到 rwe 中的属性列表中
    rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
	// 将新协议列表添加到 rwe 中的协议列表中
    rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
}
  • Primero cree una nueva lista de lista de métodos, lista de atributos y lista de protocolos y asígneles memoria, luego almacene todos los clsmétodos, atributos y protocolos clasificados, y luego transfiéralos al attachListsmétodo (las siguientes líneas de código)

¿Por qué es necesario preparar una lista de métodos?

El algoritmo de búsqueda del método es a través del algoritmo de búsqueda binaria, lo que significa sel-impque está ordenado, entonces, ¿cómo se ordena?

perpareMethodListsfixupEl método principal se llama
en fixupMethodListel método, que atravesará mlist, selestablecerá el nombre y la dirección en y luego reordenará methde acuerdo con la dirección.mlist

Esto también significa que remethodizeClassel método implementa la serialización de métodos (protocolos, etc.) en la clase.

  • attachListsEl método garantiza que se agregue al principio de la lista:
    void attachLists(List* const * addedLists, uint32_t addedCount) {
    
    
        if (addedCount == 0) return;

        if (hasArray()) {
    
    
            // many lists -> many lists
            //大数组中原本有多个小数组,再到前面加多个小数组
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
            newArray->count = newCount;
            array()->count = newCount;

            for (int i = oldCount - 1; i >= 0; i--)
                newArray->lists[i + addedCount] = array()->lists[i];
            for (unsigned i = 0; i < addedCount; i++)
                newArray->lists[i] = addedLists[i];
            free(array());
            setArray(newArray);
            validate();
        }
        //大数组中原本没有小数组,再到前面添加一个小数组
        else if (!list  &&  addedCount == 1) {
    
    
            // 0 lists -> 1 list
            list = addedLists[0];
            validate();
        } 
        //大数组中原本有一个小数组,再到前面添加多个小数组
        else {
    
    
            // 1 list -> many lists
            Ptr<List> oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            for (unsigned i = 0; i < addedCount; i++)
                array()->lists[i] = addedLists[i];
            validate();
        }
    }

El código específico para garantizar que la matriz recién agregada se implemente delante de la matriz grande es muy claro. Es una simple inserción de los elementos de la matriz. Primero, mueva los elementos originales hacia atrás según la cantidad de elementos recién agregados que necesitamos, y luego agregue los nuevos. Los elementos agregados se insertan secuencialmente comenzando desde el subíndice 0, de modo que los elementos recién agregados estén al frente.

La siguiente figura muestra vívidamente las tres situaciones de inserción anteriores:
Por favor agregue la descripción de la imagen.

Luego volvemos a él y podemos ver que load_imagestambién se llaman prepare_load_methodsfunciones y funciones call_load_methods, una es una función que se utiliza para encontrar todas las clases de carga no diferida y todos los métodos de clasificación de carga no diferida load, y la otra es loaduna función que llama a métodos para finalmente clases y clasificaciones de carga.

A continuación, echemos un vistazo a prepare_load_methodsla implementación:

Llame al método de carga para prepararse (prepare_load_methods)

void prepare_load_methods(const headerType *mhdr)
{
    
    
size_t count, i;\
runtimeLock.assertLocked();

//获取所有非懒加载类
classref_t const *classlist = 
    _getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
    
    
    schedule_class_load(remapClass(classlist[i]));
}
//获取所有非懒加载分类
category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
for (i = 0; i < count; i++) {
    
    
    category_t *cat = categorylist[i];
    Class cls = remapClass(cat->cls);
    if (!cls) continue;  // category for ignored weak-linked class
    //swift没有load方法
    if (cls->isSwiftStable()) {
    
    
        _objc_fatal("Swift class extensions and categories on Swift "
                    "classes are not allowed to have +load methods");
    }
    //实现类
    realizeClassWithoutSwift(cls, nil);
    ASSERT(cls->ISA()->isRealized());
    add_category_to_loadable_list(cat);
}

Llame al método de carga

void call_load_methods(void)
{
    
    
    static bool loading = NO;
    bool more_categories;
    //加锁:线程安全
    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
    
    
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
    
    
            call_class_loads();
        }

        // 2. Call category +loads ONCE  加载分类
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

Se puede llamar call_category_loads(); el loadmétodo de clasificación se llama en la función para cargar la clasificación final.

La gran cantidad de procesos anteriores debe resultar confuso. También puede seguir el proceso simplificado que se resume a continuación y observar la implementación específica anterior. Se verá mucho mejor.

Resumen de carga de categorías

En primer lugar, la carga de clasificación se divide en dos situaciones: clasificación de carga no diferida y clasificación de carga diferida, por lo que existen dos procesos de carga para la clasificación.

Primero hablemos del proceso de carga de categorías de carga no diferida:

  1. Ingrese load_images, ejecute loadAllCategories, loadAllCategoriesllame load_categories_nolocky vuelva load_categories_nolocka llamar addForClass
    (el proceso es: load_images–> loadAllCategories–> load_categories_nolock–> objc::unattachedCategories.addForClass)

El estado en este momento es : La clase principal correspondiente a la clasificación aún no se ha implementado (no inicializada), simplemente llamamos a la load_categories_nolockrama objc::unattachedCategories.addForClasspara asociar la clasificación con la clase principal y cargar la clasificación en la memoria.

  1. Luego de la ejecución loadAllCategories, volvemos load_imagesa ejecutar el siguiente contenido, lo siguiente que se debe ejecutar es: prepare_load_methodsel método para obtener la clase y clasificación load, en el cual se llama al método realizeClassWithoutSwiftpara implementar (inicializar) la clase principal correspondiente a la clasificación, y methodizeClassel Se llama al método en él, agregue una categoría a la clase principal y llámela nuevamente objc::unattachedCategories.attachToClass, y llámela nuevamente attachCategoriespara agregar formalmente el contenido de la clasificación a la clase principal
    (el proceso es prepare_load_methods –> realizeClassWithoutSwift –> methodizeClass –> objc::unattachedCategories.attachToClass –> attachCategories:)

El estado en este momento es : Se ha implementado la clase principal correspondiente a la clasificación y el contenido de la clasificación se ha agregado a la clase principal.

  1. Después de la ejecución prepare_load_methods, volvemos load_imagesa ejecutar el siguiente contenido. Lo que debe ejecutarse a continuación es: call_load_methodsel método se usa para llamar a los métodos de todas las clases y clasificaciones load, para que estas clases y clasificaciones de carga no diferida se puedan cargar oficialmente en el programa.
    (El proceso final es llamar a call_load_methods:)

El estado en este momento es : Se han cargado las clases de carga no diferida y sus categorías.

Hablemos del proceso de carga de categorías de carga diferida :

  1. Ingrese map_images, ejecute map_images_nolocky luego ejecute la parte relacionada con la clasificación _read_images. _read_imagesEs un juicio para juzgar si se ha ejecutado una vez load_images. Si se ha ejecutado, el valor del load_imagesparámetro del juicio didInitialAttachCategoriesserá YES, y luego ifel código en ese se puede ejecutar. Esos El código es una llamada cíclica load_categories_nolock. Para las categorías de carga diferida, sus clases principales de carga diferida correspondientes aún no se han implementado, por lo que la rama load_categories_nolocken la llamada objc::unattachedCategories.addForClassestá asociada con la categoría y la clase principal y la categoría es cargado en la memoria
    (el proceso es: map_images –> map_images_nolock –> _read_images –> load_categories_nolock –> objc::unattachedCategories.addForClass)

El estado en este momento : La clase principal correspondiente a la clasificación de carga diferida no ha sido implementada (inicializada), simplemente llamamos a la load_categories_nolockrama objc::unattachedCategories.addForClasspara asociar la clasificación con la clase principal y cargar la clasificación en la memoria.

  1. Después de ejecutar el bucle load_categories_nolock, volvemos a _read_imagesél. Lo que debe ejecutarse a continuación es la implementación (inicialización) de la clase de carga no diferida. Llamaremos realizeClassWithoutSwiftal método, pero como load_imagesya llamamos realizeClassWithoutSwiftal método e implementamos la clase no diferida. clase de carga diferida, por lo que con solo ingresar este tiempo realizeClassWithoutSwiftregresará nilsin realizar ninguna operación. Luego continuamos _read_imagesejecutando el código posterior en. Lo que necesitamos ejecutar ahora es la implementación (inicialización) de la clase de carga diferida. Todavía llamamos a la implementación de la realizeClassWithoutSwiftclase de carga diferida y llamamos methodizeClassal método para agregar categorías a la clase principal. ., se llama nuevamente objc::unattachedCategories.attachToClassy se llama nuevamente attachCategoriespara agregar formalmente el contenido de la categoría a la clase principal
    (el proceso es realizeClassWithoutSwift –> realizeClassWithoutSwift –> methodizeClass –> objc::unattachedCategories.attachToClass –> attachCategories:)

El estado en este momento : La clase principal de la categoría de carga diferida se ha implementado (inicializada) y el contenido de la categoría de carga diferida se ha agregado a la clase principal de carga diferida.

Lo anterior es mi resumen de la carga de toda la categoría. Si hay alguna pregunta, corríjame.

– inacabado

Supongo que te gusta

Origin blog.csdn.net/m0_63852285/article/details/132191855
Recomendado
Clasificación