Clasificación de categorías
Category
Es una forma de Class
extensió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
(.m
las clases en archivos no se pueden ver) Category
Puede 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
Category
se declararán los métodos , pero los desarrolladores deben implementar los métodos ellos mismos ( use objetos asociados para implementar atributos)setter
getter
setter
getter
- Al
Class
agregar atributos, los métodos se generarán e implementaránsetter
de forma predeterminada.getter
- Nota: Los atributos se pueden agregar a través de y
- La clasificación también puede hacer
framework
pú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
category
en ella (también llamado A, solo se declara, no se implementa), ahora llamoimport
a estocategory
De hecho, en este momento se llamará al método privado A. Hacemos público el método privado mediante clasificación.
- 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
- Puede
Category
volver a implementarClass
métodos que ya existen en - Puede
Category
volver a implementarCategory
métodos que ya existen/implementados en otros
[EniOS
, 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 viendoXcode>Build Phases>Compile Sources
:
- 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..m
Extension
Extension
setter&&getter
.h
Extension
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.cpp
para 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)matchPrint
e- (void)protocolMethod
implementación del método- estructura del método del objeto estructura de la lista
Siempre que sea Category
un 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)matchClass
e+ (void)protocolClassMethod
implementación de métodos de clase- Estructura de la lista de métodos de clase
Siempre que sea Category
un método de clase implementado (incluidos los métodos de clase en el proxy). _OBJC_$_CATEGORY_CLASS_METHODS_Car_$_match
se 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 person
agrega la estructura de la lista de atributos en la clasificación _OBJC_$_PROP_LIST_NSObject_$_testCategory
y no hay _ivar_list_t
una estructura de variables miembro. No hay set/get
contenido correspondiente relacionado con el método.
Esto también ilustra Category
el hecho de que las variables miembro no se pueden agregar en
resumen de categoría
Incluye principalmente las siguientes partes:
_method_list_t
Estructura de lista de métodos de tipo objeto_method_list_t
Estructura de lista de métodos de clase de tipo_protocol_list_t
estructura de lista de protocolos de tipo_prop_list_t
Tipo de estructura de lista de propiedades_category_t
La estructura no contiene_ivar_list_t
tipos, 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_init
Esta función es runtime
la función de inicialización, comencemos _objc_init
desde 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_init
al recurso, map_images
leemos el recurso ( images
que representa el módulo de recursos), llegamos a map_images_nolock
la función para buscar _read_images
la función y _read_images
buscamos 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_images
diagrama de flujo para facilitar la comprensión de todo el proceso.
Un punto importante es que al obtener el load
método de clasificación, primero obtenemos la lista de categorías de carga no diferida y luego llamamos realizeClassWithoutSwift
a 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 realizeClassWithoutSwift
el 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);
}
}
realizeClassWithoutSwift
El 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 methodizeClass
para agregar categorías a la clase principal, methodizeClass
el 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_images
efectivamente se llamó loadAllCategories
, y luego echemos un vistazo loadAllCategories
a 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_nolock
la 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
category
listalist
- Recorrer
category list
cada uno de loscategory
- Obtenga
category
la clase principal correspondientecls
. Si nocls
, 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 llamarobjc::unattachedCategories.addForClass
(load_images
Cuando la ejecución llega a este punto,objc::unattachedCategories.addForClass
se 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 lamap_images
ejecució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 enload_images
el momento de la llamada no se ha implementado la clase principal correspondiente a la clasificación, esas clases principales solo se implementan cuandoload_images
posteriormente 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).load
map_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.addForClass
aquíload_images
)objc::unattachedCategories.addForClass
para 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 semap_images
ejecuta aquí, se llamaráattachCategories
)
Definitivamente se preguntará aquí por qué load_images
la clase se agrega primero a la clase unattachedCategories
y luego la clase principal de implementación se agrega al contenido de la clase. map_images
La última llamada de read_images
la clase también es la clase se agrega primero a la clase unattachedCategories
y 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_images
agregar categorías a la clase principal maneja la carga diferida. Las categorías cargadas, y son todas iguales, primero agregue las categorías unattachedCategories
para asociar con la clase principal y almacene las categorías en la memoria, y luego espere hasta que realizeClassWithoutSwift
se llame a la función más tarde para implementar (inicializar ) la clase principal y luego llamar al methodizeClass
contenido 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_images
procesar categorías de carga diferida .map_images
read_images
Todo el map_images
proceso es aproximadamente como se muestra a continuación:
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 load
método. Si el método está implementado, load
es 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_images
de todas las clases durante la ejecución.
Luego volvemos al negocio y volvemos a load_categories_nolock
la función. Todo el proceso de la función es en realidad agregar todas las categorías a la tabla runtime_init
inicializada 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.unattachedCategories
attachCategories
unattachedCategories
attachCategories
Entonces echemos un vistazo a unattachedCategories
la 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 addForClass
es en realidad cargar la clasificación en la memoria, encontramos un try_emplace
mé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 .cls
key
lc
value
attachCategories
A 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
cls
métodos, atributos y protocolos clasificados, y luego transfiéralos alattachLists
mé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-imp
que está ordenado, entonces, ¿cómo se ordena?
perpareMethodLists
fixup
El método principal se llama
en fixupMethodList
el método, que atravesará mlist
, sel
establecerá el nombre y la dirección en y luego reordenará meth
de acuerdo con la dirección.mlist
Esto también significa que remethodizeClass
el método implementa la serialización de métodos (protocolos, etc.) en la clase.
attachLists
El 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:
Luego volvemos a él y podemos ver que load_images
también se llaman prepare_load_methods
funciones 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 load
una función que llama a métodos para finalmente clases y clasificaciones de carga.
A continuación, echemos un vistazo a prepare_load_methods
la 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 load
mé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:
- Ingrese
load_images
, ejecuteloadAllCategories
,loadAllCategories
llameload_categories_nolock
y vuelvaload_categories_nolock
a llamaraddForClass
(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_nolock
rama objc::unattachedCategories.addForClass
para asociar la clasificación con la clase principal y cargar la clasificación en la memoria.
- Luego de la ejecución
loadAllCategories
, volvemosload_images
a ejecutar el siguiente contenido, lo siguiente que se debe ejecutar es:prepare_load_methods
el método para obtener la clase y clasificaciónload
, en el cual se llama al métodorealizeClassWithoutSwift
para implementar (inicializar) la clase principal correspondiente a la clasificación, ymethodizeClass
el Se llama al método en él, agregue una categoría a la clase principal y llámela nuevamenteobjc::unattachedCategories.attachToClass
, y llámela nuevamenteattachCategories
para agregar formalmente el contenido de la clasificación a la clase principal
(el proceso esprepare_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.
- Después de la ejecución
prepare_load_methods
, volvemosload_images
a ejecutar el siguiente contenido. Lo que debe ejecutarse a continuación es:call_load_methods
el método se usa para llamar a los métodos de todas las clases y clasificacionesload
, para que estas clases y clasificaciones de carga no diferida se puedan cargar oficialmente en el programa.
(El proceso final es llamar acall_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 :
- Ingrese
map_images
, ejecutemap_images_nolock
y luego ejecute la parte relacionada con la clasificación_read_images
._read_images
Es un juicio para juzgar si se ha ejecutado una vezload_images
. Si se ha ejecutado, el valor delload_images
parámetro del juiciodidInitialAttachCategories
seráYES
, y luegoif
el código en ese se puede ejecutar. Esos El código es una llamada cíclicaload_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 ramaload_categories_nolock
en la llamadaobjc::unattachedCategories.addForClass
está 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_nolock
rama objc::unattachedCategories.addForClass
para asociar la clasificación con la clase principal y cargar la clasificación en la memoria.
- 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. LlamaremosrealizeClassWithoutSwift
al método, pero comoload_images
ya llamamosrealizeClassWithoutSwift
al método e implementamos la clase no diferida. clase de carga diferida, por lo que con solo ingresar este tiemporealizeClassWithoutSwift
regresaránil
sin realizar ninguna operación. Luego continuamos_read_images
ejecutando 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 larealizeClassWithoutSwift
clase de carga diferida y llamamosmethodizeClass
al método para agregar categorías a la clase principal. ., se llama nuevamenteobjc::unattachedCategories.attachToClass
y se llama nuevamenteattachCategories
para agregar formalmente el contenido de la categoría a la clase principal
(el proceso esrealizeClassWithoutSwift –> 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