【iOS】Kategorie, Erweiterung und verwandte Objekte

Kategorieklassifizierung

CategoryDies ist eine präzisere Erweiterungsmethode Classals 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ügenCategory
  • Es kann sogar zu Systembibliotheken/Closed-Source-Bibliotheken hinzugefügt werden, die nur Klassen in Deklarationsdateien verfügbar machen Category( .mKlassen in Dateien sind nicht sichtbar).
  • CategorySie können Instanzmethoden, Klassenmethoden und Attribute hinzufügen
    • Hinweis: Attribute können über hinzugefügt werden und Methoden Categorywerden deklariert , aber Entwickler müssen die Methoden selbst implementieren ( verwenden Sie zugehörige Objekte, um Attribute zu implementieren).settergettersettergetter
    • Durch Classdas Hinzufügen von Attributen werden setterstandardmäßig getterMethoden generiert und implementiert
  • Durch die Klassifizierung können auch frameworkprivate 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 categorydeklariert, nicht implementiert), Jetzt nenne ich importdas.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.
  • Sie Categorykönnen ClassMethoden, die bereits in vorhanden sind, erneut implementieren
  • Sie Categorykönnen Methoden erneut implementieren , die bereits in anderen [In ] Categoryvorhanden/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 :
    iOSXcode>Build Phases>Compile Sources
    Fügen Sie hier eine Bildbeschreibung ein
    • 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 .mzu anderen Klassen oder der aktuellen Klasse hinzufügen . Beim Extension
Hinzufügen zur aktuellen Klasse Extensiondeklariert der Compiler die hinzugefügten Attribute und implementiert ihre setter&&getterMethoden durch Standard.
Sie können sie auch .hüber Dateien hinzufügen. Das Hinzufügen von Klassen Extension
erfordert Extensiondie Implementierung der hinzugefügten Attribute und Methoden.
Wenn Extensiondie 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 .mDatei und keine Quellcode-Implementierung zur Dateikompilierung gibt)
Extensionist 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.cppum 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)matchPrintund - (void)protocolMethodMethodenimplementierung
  • Objektmethodenstruktur Listenstruktur

Solange es sich Categoryum 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)matchClassund + (void)protocolClassMethodImplementierung von Klassenmethoden
  • Struktur der Klassenmethodenliste

Solange es sich Categoryum eine in implementierte Klassenmethode handelt (einschließlich Klassenmethoden im Proxy). _OBJC_$_CATEGORY_CLASS_METHODS_Car_$_matchwird 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 personder Klassifizierung nur die Attributlistenstruktur _OBJC_$_PROP_LIST_NSObject_$_testCategoryund keine Mitgliedsvariablenstruktur hinzugefügt wurde _ivar_list_t. Es liegen keine entsprechenden set/getmethodenbezogenen Inhalte vor.
Dies verdeutlicht auch Categorydie Tatsache, dass Mitgliedsvariablen nicht hinzugefügt werden können

Kategoriezusammenfassung

Es umfasst hauptsächlich die folgenden Teile:

  • _method_list_tTyp-Objekt-Methodenlistenstruktur
  • _method_list_tKlassenmethodenlistenstruktur des Typs
  • _protocol_list_tProtokolllistenstruktur des Typs
  • _prop_list_tTypeigenschaftenlistenstruktur
  • _category_tDie Struktur enthält keine _ivar_list_tTypen, 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_initDiese Funktion ist runtimedie Initialisierungsfunktion. _objc_initBeginnen 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_initzur Ressource zurück, map_imageslesen die Ressource ( imagesdie das Ressourcenmodul darstellt), kommen zur map_images_nolockFunktion, um die Funktion zu finden _read_images, und _read_imagessuchen 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_imagesFlussdiagramm erstellt, um das Verständnis des gesamten Prozesses zu erleichtern.
Fügen Sie hier eine Bildbeschreibung ein

Ein wichtiger Punkt ist, dass wir beim Erhalten der Klassifizierungsmethode loadzuerst die Liste der nicht verzögerten Ladekategorien erhalten und dann realizeClassWithoutSwiftdie Hauptklasse der Klassifizierung aufrufen, um sie zu implementieren. Dies ist sehr wichtig. In der Zusammenfassung des gesamten Prozesses wird später erwähnt, dass realizeClassWithoutSwiftder 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 realizeClassWithoutSwiftQuellcode 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, methodizeClassum der Hauptklasse Kategorien hinzuzufügen. methodizeClassDer 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_imagestatsächlich aufgerufen wurde loadAllCategories, und dann werfen wir einen Blick loadAllCategoriesauf 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_nolockdie 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));
}
  • categoryListe abrufenlist
  • Durchqueren Sie category listjedes dercategory
  • Holen Sie sich categorydie entsprechende Hauptklasse cls. Wenn nicht cls, ü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 Aufruf objc::unattachedCategories.addForClass( load_imagesWenn die Ausführung diesen Punkt erreicht, objc::unattachedCategories.addForClasswird sie aufgerufen, um die Kategorie in ihrer entsprechenden Hauptklasse zu registrieren, um die anschließende Rekonstruktion der Methodenliste der Klasse zu erleichtern. Wenn die map_imagesAusführung diesen Punkt erreicht, wird sie aufgerufen attachCategories. Die Der Grund, warum die aufrufenden Zweige in diesen beiden Fällen unterschiedlich sind, liegt darin, dass load_imagesdie der Klassifizierung entsprechende Hauptklasse beim Aufruf nicht implementiert wurde. Diese Hauptklassen werden erst implementiert, wenn die Methode load_imageszum 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.)loadmap_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.addForClassAusführung load_imageshier objc::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 sie map_imageshier ausgeführt wird, wird sie aufgerufen attachCategories)

Sie werden sich hier definitiv fragen, warum load_imagesdie Klasse zuerst zur Klasse hinzugefügt wird unattachedCategoriesund dann die Implementierung der Hauptklasse zum Klasseninhalt hinzugefügt wird. map_imagesDer letzte Aufruf der read_imagesKlasse 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_imageskann 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_imageserfolgt Kategorien in die Hauptklasse übernehmen das verzögerte Laden. Geladene Kategorien sind alle gleich. Fügen Sie zunächst die Kategorien hinzu, die unattachedCategoriesmit der Hauptklasse verknüpft werden sollen, und speichern Sie die Kategorien im Speicher. Warten Sie dann, bis realizeClassWithoutSwiftdie Funktion später aufgerufen wird, um sie zu implementieren (initialisieren). ) Die Hauptklasse und dann methodizeClassden 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_imagesVerarbeiten von Kategorien mit verzögertem Laden .map_imagesread_images

Der gesamte map_imagesProzess läuft ungefähr wie folgt ab:

Bitte fügen Sie eine Bildbeschreibung hinzu

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 loadMethode implementiert. Wenn die Methode implementiert ist, loadhandelt 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_imagesaller Klassen während der Ausführung.

Dann kehren wir zum Geschäft zurück und kehren zur load_categories_nolockFunktion zurück. Der gesamte Funktionsprozess besteht tatsächlich darin, alle Kategorien zur in runtime_initinitialisierten unattachedCategoriesTabelle hinzuzufügen oder attachCategoriesden Inhalt der Kategorie direkt zur Hauptklasse hinzuzufügen. Beachten Sie, dass unattachedCategoriesdie 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 attachCategoriesspäter Vorgänge ausführen müssen, um sie in die Hauptkategorie einzufügen.

Dann schauen wir uns unattachedCategoriesdie 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 addForClassist tatsächlich das Laden der Klassifizierung in den Speicher. Wir haben try_emplacedarin 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) .clskeylcvalue

attachCategoriesSchauen 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 clsMethoden, Attribute und Protokolle und übertragen Sie sie dann auf attachListsdie 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-impsie sortiert ist. Wie wird sie also sortiert?

perpareMethodListsfixupDie Hauptmethode wird
in der Methode aufgerufen , fixupMethodListdie den Namen und die Adresse durchläuft und dann entsprechend der Adresse neu anordnet .mlistselmethmlist

Dies bedeutet auch, dass remethodizeClassdie Methode die Serialisierung von Methoden (Protokollen usw.) in der Klasse implementiert

  • attachListsDie 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:
Bitte fügen Sie eine Bildbeschreibung hinzu

Dann kehren wir dorthin zurück und können sehen , dass Funktionen und Funktionen load_imagesebenfalls 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_methodscall_load_methodsloadload

Schauen wir uns als Nächstes prepare_load_methodsdie 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 loadwird 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:

  1. Geben Sie ein load_images, führen Sie aus loadAllCategories, loadAllCategoriesrufen Sie auf load_categories_nolockund load_categories_nolockrufen Sie erneut auf addForClass
    (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_nolockum objc::unattachedCategories.addForClassdie Klassifizierung mit der Hauptklasse zu verknüpfen und die Klassifizierung in den Speicher zu laden.

  1. Nach der Ausführung loadAllCategorieskehren wir zurück, load_imagesum den folgenden Inhalt auszuführen. Als Nächstes müssen ausgeführt werden: prepare_load_methodsdie Methode zum Erhalten der Klasse und der Klassifizierung load, in der die Methode aufgerufen wird, realizeClassWithoutSwiftum 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.attachToClassRufen Sie sie erneut auf, attachCategoriesum 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.

  1. Nach der Ausführung prepare_load_methodskehren wir load_imageszur Ausführung des folgenden Inhalts zurück. Als Nächstes muss Folgendes ausgeführt werden: call_load_methodsDie Methode wird zum Aufrufen der Methoden aller Klassen und Klassifizierungen verwendet load, 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 :

  1. Geben Sie ein map_images, führen Sie den Teil aus, der sich auf die Klassifizierung bezieht , map_images_nolockund 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_imagesload_imagesload_imagesdidInitialAttachCategoriesYESifload_categories_nolockload_categories_nolockobjc::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_nolockum objc::unattachedCategories.addForClassdie Klassifizierung mit der Hauptklasse zu verknüpfen und die Klassifizierung in den Speicher zu laden.

  1. Nachdem die Schleife ausgeführt wurde load_categories_nolock, kehren wir zu _read_imagesihr zurück. Was als nächstes ausgeführt werden muss, ist die Implementierung (Initialisierung) der nicht verzögerten Ladeklasse. Wir werden realizeClassWithoutSwiftdie Methode aufrufen, aber da wir die Methode load_imagesbereits aufgerufen realizeClassWithoutSwiftund die nicht verzögerte Ladeklasse implementiert haben Lazy-Loading-Klasse. Wenn Sie also nur diese Zeit eingeben, realizeClassWithoutSwiftwerden nilkeine Operationen ausgeführt. Dann führen wir _read_imagesden nachfolgenden Code weiter aus. Was wir jetzt ausführen müssen, ist die Implementierung (Initialisierung) der Lazy-Loading-Klasse. Wir rufen weiterhin die Implementierung der realizeClassWithoutSwiftLazy-Loading-Klasse auf und rufen methodizeClassdie Methode auf, um der Hauptklasse Kategorien hinzuzufügen ., es wird erneut aufgerufen objc::unattachedCategories.attachToClass, und es wird erneut aufgerufen attachCategories, um den Inhalt der Kategorie formell zur Hauptklasse hinzuzufügen
    (der Prozess ist realizeClassWithoutSwift –> 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

Ich denke du magst

Origin blog.csdn.net/m0_63852285/article/details/132191855
Empfohlen
Rangfolge