Kategorieklassifizierung
Category
Dies ist eine präzisere Erweiterungsmethode Class
als die Vererbung, und Sie können Methoden dynamisch zu vorhandenen Klassen hinzufügen, ohne Unterklassen zu erstellen.
- Sie können es zu jeder vorhandenen Klasse im Projekt hinzufügen
Category
- Es kann sogar zu Systembibliotheken/Closed-Source-Bibliotheken hinzugefügt werden, die nur Klassen in Deklarationsdateien verfügbar machen
Category
(.m
Klassen in Dateien sind nicht sichtbar). Category
Sie können Instanzmethoden, Klassenmethoden und Attribute hinzufügen- Hinweis: Attribute können über hinzugefügt werden und Methoden
Category
werden deklariert , aber Entwickler müssen die Methoden selbst implementieren ( verwenden Sie zugehörige Objekte, um Attribute zu implementieren).setter
getter
setter
getter
- Durch
Class
das Hinzufügen von Attributen werdensetter
standardmäßiggetter
Methoden generiert und implementiert
- Hinweis: Attribute können über hinzugefügt werden und Methoden
- Durch die Klassifizierung können auch
framework
private Methoden öffentlich gemacht werden- Wenn wir beispielsweise eine Klasse mit einer privaten Methode A haben, die von der Außenwelt nicht aufgerufen werden kann, ich aber eine Methode für diese Klasse schreibe und darin eine Methode deklariere (auch A genannt, wird sie nur
category
deklariert, nicht implementiert), Jetzt nenne ichimport
das.category
Wie ist die Situation von A? Tatsächlich wird zu diesem Zeitpunkt die private Methode A aufgerufen. Wir machen die private Methode durch Klassifizierung öffentlich.
- Wenn wir beispielsweise eine Klasse mit einer privaten Methode A haben, die von der Außenwelt nicht aufgerufen werden kann, ich aber eine Methode für diese Klasse schreibe und darin eine Methode deklariere (auch A genannt, wird sie nur
- Sie
Category
könnenClass
Methoden, die bereits in vorhanden sind, erneut implementieren - Sie
Category
können Methoden erneut implementieren , die bereits in anderen [In ]Category
vorhanden/implementiert sind . Die Reihenfolge des Aufrufs von Instanzobjekten/Klassenobjektmethoden hängt streng von der Kompilierungsreihenfolge der Quellcodedatei ab. Die Kompilierungsreihenfolge kann angezeigt werden, indem Folgendes angezeigt wird :iOS
Xcode>Build Phases>Compile Sources
- Klassen und jede Kategorie deklarieren und implementieren ihre eigenen Methoden: Es wird keine Methodenimplementierung überschrieben und Kategorien erweitern lediglich die Funktionalität der Klasse.
- Die Klasse und jede Kategorie verfügen über Deklarationen und implementierte Methoden mit demselben Namen: Die Implementierung der vorhandenen Methode wird überschrieben (tatsächlich wird sie nicht überschrieben, aber die Methodenadresse wird später verschoben. Das System findet die Methode mit demselben Namen früher in der Speicheradresse, um den Aufruf zu implementieren)
- Implementierungspriorität der Klassifizierungsmethode > Originalklasse
- Die Abdeckung in jeder Kategorie hängt streng von der Kompilierungsreihenfolge der Quellcodedateien ab:
- Die zuerst kompilierte Methode wird zuerst zur Methodenliste hinzugefügt und „zuerst auf den Stapel gelegt“.
- Die nachkompilierte Methode wird zur Methodenliste „Post-Stack“ hinzugefügt.
- Wenn das System die Implementierung einer Methode aufruft, sendet es unten Nachrichten über Objekte (Instanzobjekte, Klassenobjekte usw.) und Methoden-APIs und ruft den Implementierungs-IMP-Zeiger der Methodenimplementierung ab, um die spezifische Implementierung der Methode zu finden (Eigentlich ist es die endgültige erhaltene Methodenimplementierung. Es handelt sich um die Methodenimplementierung in der nachkompilierten Quellcodedatei.)
Die offizielle Einführung bietet zwei Vorteile:
- Die Implementierung der Klasse kann in mehrere verschiedene Dateien aufgeteilt werden
- Kann die Größe einzelner Dateien reduzieren
- Verschiedene Funktionen können in verschiedene Kategorien organisiert werden
- Mehrere Entwickler können zusammenarbeiten, um einen Kurs abzuschließen
- Sie können die gewünschten Kategorien bei Bedarf usw. laden.
- Deklarieren Sie eine proprietäre Methode
Verlängerung
Erweiterung ( Extension
) kann als anonym verstanden werdenCategory
Deklarationen, die zum Hinzufügen von Attributen und Methoden zu einer Klasse verwendet werden können, funktionieren nicht. Sie können sie Subclass
über Dateien .m
zu anderen Klassen oder der aktuellen Klasse hinzufügen . Beim Extension
Hinzufügen zur aktuellen Klasse Extension
deklariert der Compiler die hinzugefügten Attribute und implementiert ihre setter&&getter
Methoden durch Standard.
Sie können sie auch .h
über Dateien hinzufügen. Das Hinzufügen von Klassen Extension
erfordert Extension
die Implementierung der hinzugefügten Attribute und Methoden.
Wenn Extension
die vorhandenen Methoden in der ursprünglichen Klasse oder anderen Kategorien in der Implementierung erneut implementiert werden, hat dies keinen Einfluss auf die Ausführung des Originals Die Methode (da es keine eigene .m
Datei und keine Quellcode-Implementierung zur Dateikompilierung gibt)
Extension
ist nützlicher, um Klassen mit komplexen Strukturen aufzuteilen und Deklarationen klarer darzustellen.
Die Essenz der Kategorie
Kategoriestruktur
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);
};
In der klassifizierten Struktur können der Klasse Objektmethoden, Klassenmethoden, Protokolle und Attribute hinzugefügt werden, es gibt jedoch keine Mitgliedsvariablen.
Konvertieren Sie die Klassifizierung in C++
Dann konvertieren wir die klassifizierte .m-Datei in eine C++-Datei, um Folgendes herauszufinden:
Wir erstellen zunächst eine Kategorie:
#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
Wir haben eine Instanzmethode, eine Klassenmethode und ein Attribut darin deklariert.
Die Klassifizierung folgt einem Protokoll, und das Protokoll enthält auch eine Klassenmethode und eine Objektmethode.
#ifndef protocolForCar_h
#define protocolForCar_h
@protocol CarProtocol <NSObject>
- (void)protocolMethod;
+ (void)protocolClassMethod;
@end
Verwenden Sie dann:, xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Car+match.m -o test.cpp
um die .m-Datei dieser Kategorie in eine C++-Datei zu konvertieren:
//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,
};
Wir können die drei Elemente des Fokus erkennen
- Kategoriestruktur
- Zuweisungsanweisung der Kategoriestruktur
- Kategoriestruktur-Array
Struktur der Objektmethodenliste
//本类对象方法的实现
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
und- (void)protocolMethod
Methodenimplementierung- Objektmethodenstruktur Listenstruktur
Solange es sich Category
um eine in implementierte Objektmethode handelt (einschließlich Objektmethoden im Proxy). wird zur Objektmethodenlistenstruktur hinzugefügt _OBJC_$_CATEGORY_INSTANCE_METHODS_Car_$_match
. Wenn es sich nur um eine Definition handelt, die jedoch nicht implementiert ist, wird sie nicht hinzugefügt.
Struktur der Klassenmethodenliste
//本类类方法的实现
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
und+ (void)protocolClassMethod
Implementierung von Klassenmethoden- Struktur der Klassenmethodenliste
Solange es sich Category
um eine in implementierte Klassenmethode handelt (einschließlich Klassenmethoden im Proxy). _OBJC_$_CATEGORY_CLASS_METHODS_Car_$_match
wird zur Klassenmethodenlistenstruktur hinzugefügt.
Struktur der Protokollliste
//协议结构体
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;
Struktur der Eigenschaftsliste
//属性结构体
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"}}
};
Aus dem Quellcode der Attributlistenstruktur können wir ersehen, dass person
der Klassifizierung nur die Attributlistenstruktur _OBJC_$_PROP_LIST_NSObject_$_testCategory
und keine Mitgliedsvariablenstruktur hinzugefügt wurde _ivar_list_t
. Es liegen keine entsprechenden set/get
methodenbezogenen Inhalte vor.
Dies verdeutlicht auch Category
die Tatsache, dass Mitgliedsvariablen nicht hinzugefügt werden können
Kategoriezusammenfassung
Es umfasst hauptsächlich die folgenden Teile:
_method_list_t
Typ-Objekt-Methodenlistenstruktur_method_list_t
Klassenmethodenlistenstruktur des Typs_protocol_list_t
Protokolllistenstruktur des Typs_prop_list_t
Typeigenschaftenlistenstruktur_category_t
Die Struktur enthält keine_ivar_list_t
Typen, das heißt, sie enthält keine Mitgliedsvariablenstrukturen.
Was bewirkt die Klassifizierung zur Laufzeit?
(Dieser Teil des Inhalts ist relativ aufgebläht. Sie können sich zuerst auf die Zusammenfassung des Ladens der Kategorie am Ende des Artikels beziehen und dann die Zusammenfassung hier mit der Quellcode-Analyse kombinieren, um sie logischer zu gestalten.)
Um dieses Problem zu verstehen, müssen wir wissen, wann die Klassifizierungsmethode aufgerufen wird
_objc_init
Diese Funktion ist runtime
die Initialisierungsfunktion. _objc_init
Beginnen wir von vorne:
_objc_heat
/***********************************************************************
* _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
}
Darunter haben wir einige Initialisierungserstellungsprozesse gefunden. Hier konzentrieren wir uns hauptsächlich auf runtime_init:
void runtime_init(void)
{
//分类加载表
objc::unattachedCategories.init(32);
//类的加载表
objc::allocatedClasses.init();
}
Sie können sehen, dass es eine Kategorieladetabelle gibt.
Dann kehren wir _objc_init
zur Ressource zurück, map_images
lesen die Ressource ( images
die das Ressourcenmodul darstellt), kommen zur map_images_nolock
Funktion, um die Funktion zu finden _read_images
, und _read_images
suchen den klassifizierungsbezogenen Code in der Funktion:
_read_images
// 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");
Bilder laden
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();
}
Wir haben ein load_images
Flussdiagramm erstellt, um das Verständnis des gesamten Prozesses zu erleichtern.
Ein wichtiger Punkt ist, dass wir beim Erhalten der Klassifizierungsmethode load
zuerst die Liste der nicht verzögerten Ladekategorien erhalten und dann realizeClassWithoutSwift
die Hauptklasse der Klassifizierung aufrufen, um sie zu implementieren. Dies ist sehr wichtig. In der Zusammenfassung des gesamten Prozesses wird später erwähnt, dass realizeClassWithoutSwift
der Quellcode des Aufrufs wie folgt lautet:
//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);
}
}
Der zur Implementierung der Hauptklassenmethode verwendete realizeClassWithoutSwift
Quellcode lautet wie folgt:
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;
}
Sie können sehen, dass schließlich eine Funktion ausgeführt wird, methodizeClass
um der Hauptklasse Kategorien hinzuzufügen. methodizeClass
Der Quellcode lautet wie folgt:
//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
}
Und wir haben herausgefunden , dass die Funktion load_images
tatsächlich aufgerufen wurde loadAllCategories
, und dann werfen wir einen Blick loadAllCategories
auf die Implementierung:
LoadAllCategories
static void loadAllCategories() {
mutex_locker_t lock(runtimeLock);
for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) {
//调用load_categories_nolock来加载分类
load_categories_nolock(hi);
}
}
Schauen wir uns als Nächstes load_categories_nolock
die Implementierung der Funktion an (diese Methode wird in beiden Prozessen aufgerufen und der Ausführungsinhalt unterscheidet sich geringfügig) load_images
:map_images
Load_categories_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));
}
category
Liste abrufenlist
- Durchqueren Sie
category list
jedes dercategory
- Holen Sie sich
category
die entsprechende Hauptklassecls
. Wenn nichtcls
, überspringen Sie (continue
) und fahren Sie mit dem Abrufen der nächsten fort
(wenn die der Klassifizierung entsprechende Hauptklasse ein Klassenobjekt ist). Wenn es eine entsprechende Hauptklasse und Instanzmethoden, Protokolle usw. hat Attribute, dann Aufrufobjc::unattachedCategories.addForClass
(load_images
Wenn die Ausführung diesen Punkt erreicht,objc::unattachedCategories.addForClass
wird sie aufgerufen, um die Kategorie in ihrer entsprechenden Hauptklasse zu registrieren, um die anschließende Rekonstruktion der Methodenliste der Klasse zu erleichtern. Wenn diemap_images
Ausführung diesen Punkt erreicht, wird sie aufgerufenattachCategories
. Die Der Grund, warum die aufrufenden Zweige in diesen beiden Fällen unterschiedlich sind, liegt darin, dassload_images
die der Klassifizierung entsprechende Hauptklasse beim Aufruf nicht implementiert wurde. Diese Hauptklassen werden erst implementiert, wenn die Methodeload_images
zum Erhalten der Klassifizierung anschließend abgerufen wird . Wie Als Ergebnis wurde die Hauptklasse, die der Klassifizierung entspricht, bereits implementiert, als die Ausführung dort ankam, sodass der if-Zweig unterschiedlich ist.)load
map_images
- (Wenn die der Klassifizierung entsprechende Hauptklasse ein Metaklassenobjekt ist) Wenn es eine entsprechende Hauptklasse und Klassenmethoden und -protokolle hat, rufen Sie (bei der
objc::unattachedCategories.addForClass
Ausführungload_images
hierobjc::unattachedCategories.addForClass
) auf, um die Klassifizierung in der entsprechenden Hauptklasse zu registrieren. Das ist praktisch um später die Methodenliste der Klasse zu rekonstruieren. Wenn siemap_images
hier ausgeführt wird, wird sie aufgerufenattachCategories
)
Sie werden sich hier definitiv fragen, warum load_images
die Klasse zuerst zur Klasse hinzugefügt wird unattachedCategories
und dann die Implementierung der Hauptklasse zum Klasseninhalt hinzugefügt wird. map_images
Der letzte Aufruf der read_images
Klasse ist auch die Klasse, die zuerst zur Klasse hinzugefügt wird unattachedCategories
, und dann die Implementierung der Hauptklasse Die Klasse wird dem Klasseninhalt hinzugefügt. Tatsächlich map_images
kann die Klasse aus den Kommentaren über dem Code verstanden werden, der die Klassifizierung behandelt. In unseren Load_Images behandelt das Hinzufügen von Kategorien zur Hauptklasse das nicht verzögerte Laden von Kategorien, während in der Mitte das Hinzufügen read_images
erfolgt Kategorien in die Hauptklasse übernehmen das verzögerte Laden. Geladene Kategorien sind alle gleich. Fügen Sie zunächst die Kategorien hinzu, die unattachedCategories
mit der Hauptklasse verknüpft werden sollen, und speichern Sie die Kategorien im Speicher. Warten Sie dann, bis realizeClassWithoutSwift
die Funktion später aufgerufen wird, um sie zu implementieren (initialisieren). ) Die Hauptklasse und dann methodizeClass
den spezifischen Klassifizierungsinhalt aufrufen, der implementiert wird. Zur Hauptklasse hinzufügen
Kurz gesagt, es gibt zwei Pfade zum Laden von Kategorien: Einer ist der Pfad zum Verarbeiten von Kategorien mit nicht verzögertem Laden und der andere ist der Pfad zum load_images
Verarbeiten von Kategorien mit verzögertem Laden .map_images
read_images
Der gesamte map_images
Prozess läuft ungefähr wie folgt ab:
Interessanterweise ruft Schritt 10 oben, die Initialisierung der Lazy-Loading-Klasse, tatsächlich das auf, was wir oben gesagt haben realizeClassWithoutSwift
.
Darüber hinaus besteht der Unterschied zwischen verzögert geladenen Klassen und nicht verzögert geladenen Klassen darin, ob die aktuelle Klasse die load
Methode implementiert. Wenn die Methode implementiert ist, load
handelt es sich um eine nicht verzögert geladene Klasse und umgekehrt. Außerdem ist die Das Laden der Daten der verzögert geladenen Klasse wird verschoben, bis sie zum ersten Mal empfangen werden. Das Laden beginnt nur, wenn eine Nachricht gesendet wird, und nicht verzögert geladene Klassen laden die Daten map_images
aller Klassen während der Ausführung.
Dann kehren wir zum Geschäft zurück und kehren zur load_categories_nolock
Funktion zurück. Der gesamte Funktionsprozess besteht tatsächlich darin, alle Kategorien zur in runtime_init
initialisierten unattachedCategories
Tabelle hinzuzufügen oder attachCategories
den Inhalt der Kategorie direkt zur Hauptklasse hinzuzufügen. Beachten Sie, dass unattachedCategories
die Tabelle hier den Inhalt bedeutet der Kategorie wird nicht zur Hauptklasse hinzugefügt. Fügen Sie die klassifizierten Tabellen in die Hauptkategorie ein, was darauf hinweist, dass Sie attachCategories
später Vorgänge ausführen müssen, um sie in die Hauptkategorie einzufügen.
Dann schauen wir uns unattachedCategories
die Tabelle an:
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);
}
};
Eine davon addForClass
ist tatsächlich das Laden der Klassifizierung in den Speicher. Wir haben try_emplace
darin eine Methode gefunden, deren Code wie folgt lautet:
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);
}
Dadurch wird eine Bucket-Struktur erstellt (dies ist eine Schlüssel-Wert-Paar-Struktur) und der Inhalt darin gespeichert. In Kombination mit dem obigen Funktionsaufruf wissen wir, dass es sich um eine Speicherung handeltget().try_emplace(cls, lc)
.cls
key
lc
value
attachCategories
Schauen wir uns als Nächstes den Quellcode der Funktion an , die der Hauptklasse Inhalte in der Kategorie hinzufügt :
// 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);
}
- Erstellen Sie zunächst eine neue Liste mit Methodenlisten, Attributlisten und Protokolllisten und weisen Sie ihnen Speicher zu. Speichern Sie dann alle klassifizierten
cls
Methoden, Attribute und Protokolle und übertragen Sie sie dann aufattachLists
die Methode (die folgenden Codezeilen).
Warum ist es notwendig, eine Methodenliste zu erstellen?
Der Suchalgorithmus der Methode erfolgt über den binären Suchalgorithmus, was bedeutet, dass sel-imp
sie sortiert ist. Wie wird sie also sortiert?
perpareMethodLists
fixup
Die Hauptmethode wird
in der Methode aufgerufen , fixupMethodList
die den Namen und die Adresse durchläuft und dann entsprechend der Adresse neu anordnet .mlist
sel
meth
mlist
Dies bedeutet auch, dass remethodizeClass
die Methode die Serialisierung von Methoden (Protokollen usw.) in der Klasse implementiert
attachLists
Die Methode stellt sicher, dass sie am Anfang der Liste hinzugefügt wird:
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();
}
}
Der spezifische Code, der sicherstellt, dass das neu hinzugefügte Array vor dem großen Array implementiert wird, ist sehr klar. Es handelt sich um ein einfaches Einfügen der Array-Elemente. Verschieben Sie zunächst die ursprünglichen Elemente um die Anzahl der neu hinzugefügten Elemente, die wir benötigen, nach hinten Fügen Sie dann die neuen hinzu. Die hinzugefügten Elemente werden der Reihe nach ab Index 0 eingefügt, sodass die neu hinzugefügten Elemente vorne stehen.
Die folgende Abbildung zeigt anschaulich die oben genannten drei Einfügungssituationen:
Dann kehren wir dorthin zurück und können sehen , dass Funktionen und Funktionen load_images
ebenfalls aufgerufen werden . Eine ist eine Funktion, die zum Finden aller nicht verzögerten Ladeklassen und nicht verzögerten Ladeklassifizierungsmethoden verwendet wird , und die andere ist eine Funktion, die Methoden aufruft, um sie schließlich aufzurufen Belastungsklassen und Klassifizierungen. .prepare_load_methods
call_load_methods
load
load
Schauen wir uns als Nächstes prepare_load_methods
die Implementierung an:
Rufen Sie die Lademethode zur Vorbereitung auf (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);
}
Rufen Sie die Lademethode auf
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;
}
Es kann aufgerufen werden call_category_loads()
; die Klassifizierungsmethode load
wird in der Funktion aufgerufen, um die endgültige Klassifizierung zu laden.
Die große Anzahl der oben genannten Prozesse muss verwirrend sein. Sie können auch dem unten zusammengefassten optimierten Prozess folgen, um die spezifische Implementierung oben zu sehen. Sie wird viel besser aussehen.
Zusammenfassung des Ladens von Kategorien
Zunächst wird das Laden der Klassifizierung in zwei Situationen unterteilt: Klassifizierung ohne verzögertes Laden und Klassifizierung mit verzögertem Laden, sodass es zwei Ladeprozesse für die Klassifizierung gibt.
Lassen Sie uns zunächst über den Ladevorgang von Nicht-Lazy-Loading-Kategorien sprechen:
- Geben Sie ein
load_images
, führen Sie ausloadAllCategories
,loadAllCategories
rufen Sie aufload_categories_nolock
undload_categories_nolock
rufen Sie erneut aufaddForClass
(der Vorgang ist:load_images
–>loadAllCategories
–>load_categories_nolock
–>objc::unattachedCategories.addForClass
)
Der Status zu diesem Zeitpunkt lautet : Die der Klassifizierung entsprechende Hauptklasse wurde noch nicht implementiert (nicht initialisiert). Wir haben lediglich den Zweig aufgerufen, load_categories_nolock
um objc::unattachedCategories.addForClass
die Klassifizierung mit der Hauptklasse zu verknüpfen und die Klassifizierung in den Speicher zu laden.
- Nach der Ausführung
loadAllCategories
kehren wir zurück,load_images
um den folgenden Inhalt auszuführen. Als Nächstes müssen ausgeführt werden:prepare_load_methods
die Methode zum Erhalten der Klasse und der Klassifizierungload
, in der die Methode aufgerufen wird,realizeClassWithoutSwift
um die der Klassifizierung entsprechende Hauptklasse zu implementieren (zu initialisieren)methodizeClass
. Die Methode wird darin aufgerufen. Fügen Sie der Hauptklasse eine Kategorie hinzu und rufen Sie sie erneut auf.objc::unattachedCategories.attachToClass
Rufen Sie sie erneut auf,attachCategories
um den Inhalt der Klassifizierung formal zur Hauptklasse hinzuzufügen
(der Prozess ist:prepare_load_methods –> realizeClassWithoutSwift –> methodizeClass –> objc::unattachedCategories.attachToClass –> attachCategories
)
Der Status zu diesem Zeitpunkt lautet : Die der Klassifizierung entsprechende Hauptklasse wurde implementiert und der Inhalt der Klassifizierung wurde der Hauptklasse hinzugefügt.
- Nach der Ausführung
prepare_load_methods
kehren wirload_images
zur Ausführung des folgenden Inhalts zurück. Als Nächstes muss Folgendes ausgeführt werden:call_load_methods
Die Methode wird zum Aufrufen der Methoden aller Klassen und Klassifizierungen verwendetload
, sodass diese nicht verzögert geladenen Klassen und Klassifizierungen offiziell in die geladen werden können Programm.
(Der letzte Vorgang besteht darin, Folgendes anzurufen:call_load_methods
)
Der Status ist derzeit : Nicht-Lazy-Loading-Klassen und ihre Kategorien wurden geladen.
Lassen Sie uns über den Ladevorgang von Lazy-Loading-Kategorien sprechen :
- Geben Sie ein
map_images
, führen Sie den Teil aus, der sich auf die Klassifizierung bezieht ,map_images_nolock
und führen Sie ihn dann aus . Es handelt sich um eine Beurteilung, ob es einmal ausgeführt wurde . Wenn es ausgeführt wurde, lautet der Wert des Parameters der Beurteilung und dann der darin enthaltene Code kann ausgeführt werden. Diese Der Code ist ein zyklischer Aufruf . Für verzögert geladene Kategorien wurden die entsprechenden verzögert geladenen Hauptklassen noch nicht implementiert, sodass der Zweig im Aufruf der Kategorie und der Hauptklasse zugeordnet ist und die Kategorie vorhanden ist in den Speicher geladen (der Vorgang ist: )_read_images
_read_images
load_images
load_images
didInitialAttachCategories
YES
if
load_categories_nolock
load_categories_nolock
objc::unattachedCategories.addForClass
map_images –> map_images_nolock –> _read_images –> load_categories_nolock –> objc::unattachedCategories.addForClass
Der Status zu diesem Zeitpunkt : Die der Lazy-Loading-Klassifizierung entsprechende Hauptklasse wurde nicht implementiert (initialisiert). Wir haben lediglich den Zweig aufgerufen, load_categories_nolock
um objc::unattachedCategories.addForClass
die Klassifizierung mit der Hauptklasse zu verknüpfen und die Klassifizierung in den Speicher zu laden.
- Nachdem die Schleife ausgeführt wurde
load_categories_nolock
, kehren wir zu_read_images
ihr zurück. Was als nächstes ausgeführt werden muss, ist die Implementierung (Initialisierung) der nicht verzögerten Ladeklasse. Wir werdenrealizeClassWithoutSwift
die Methode aufrufen, aber da wir die Methodeload_images
bereits aufgerufenrealizeClassWithoutSwift
und die nicht verzögerte Ladeklasse implementiert haben Lazy-Loading-Klasse. Wenn Sie also nur diese Zeit eingeben,realizeClassWithoutSwift
werdennil
keine Operationen ausgeführt. Dann führen wir_read_images
den nachfolgenden Code weiter aus. Was wir jetzt ausführen müssen, ist die Implementierung (Initialisierung) der Lazy-Loading-Klasse. Wir rufen weiterhin die Implementierung derrealizeClassWithoutSwift
Lazy-Loading-Klasse auf und rufenmethodizeClass
die Methode auf, um der Hauptklasse Kategorien hinzuzufügen ., es wird erneut aufgerufenobjc::unattachedCategories.attachToClass
, und es wird erneut aufgerufenattachCategories
, um den Inhalt der Kategorie formell zur Hauptklasse hinzuzufügen
(der Prozess istrealizeClassWithoutSwift –> realizeClassWithoutSwift –> methodizeClass –> objc::unattachedCategories.attachToClass –> attachCategories
:)
Der Status zu diesem Zeitpunkt : Die Hauptklasse der Lazy-Loading-Kategorie wurde implementiert (initialisiert) und der Inhalt in der Lazy-Loading-Kategorie wurde der Lazy-Loading-Hauptklasse hinzugefügt.
Das Obige ist meine Zusammenfassung der Auslastung der gesamten Kategorie. Wenn Sie Fragen haben, korrigieren Sie mich bitte.
– unvollendet