2019-10-15
This article describes the implementation of categories and the loading process of categories. Classification is an extension of objective-C classes. When it comes to classification, there is no mention of Extension. Extensions are often thought of as anonymous classifications, but the difference between the two implementations is significant:
- Extensions are just extensions to the interface; all implementations are in the class
@implementation
In the block, the classification is an extension of the interface and the implementation of the classification in@implementation(CategoryName)
Block; - The classification is in Runtime
category_t
Structures correspond to them, extensions do not; - An extension is a compile-time decision, a classification is a run-time decision, and the classification is loaded into the method list during the run-load phase.
Note: Classification is a decorator pattern. The advantage of class extensions is that an extension to a parent class can be applied directly to all of its derived classes.
1. Data structure
1.1 Definition of classification
The classified data structure is the category_t structure. Contains the class name, CLS, instanceMethods, classMethods, protocols, instanceProperties, and instanceMethods.
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;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta) {
if (isMeta) return nil; // classProperties;
else returninstanceProperties; }};Copy the code
1.2 Category List
Classes do not contain data structures such as a list of categories, the category_t structure is just to record the categories defined by the developer at compile time and store them in a specific container. However, the program itself needs to keep the list of categories, because when loading the program, it needs to load the categories according to the categories recorded in the container. The container that holds all the categories defined by the application is category_list, which is also an alias to locstamped_category_list_t. Locstamped_category_list_t is a sequential table container with the locstamped_category_T structure as the element. The locstamped_category_t structure contains the CAT member that points to the category_t structure.
// The category list category_list is an alias for locstamped_category_list_t typedef locstamped_category_list_t category_list; Struct locstamped_category_t {category_t *cat; struct header_info *hi; // Ignore}; Struct locstamped_category_list_t {uint32_t count; // Uint32_t count;#if __LP64__
uint32_t reserved;
#endif
locstamped_category_t list[0];
};
Copy the code
Second, classification loading
The class is loaded during the application loading phase. After the application finishes loading the basic information of the class (the information determined at compile time), the elements such as the list of defined attributes and the list of methods in all classes of the class need to be added to the class_rw_t of the class.
The core code for classification loading is in _read_images(…) Logic. The key link includes: 1, call addUnattachedCategoryForClass (…). Adds the category to the class’s list of unprocessed categories; RemethodizeClass (…) The two processes exist in pairs based on the list of methods for refactoring classes by category. The former collects all the unloaded list of categories, and the latter loads information such as a list of methods, a list of attributes, and so on from the unloaded list of categories into the class.
void _read_images(header_info **hList, uint32_t hCount)
{
#define EACH_HEADER \hIndex = 0; \ crashlog_header_name(nil) && hIndex < hCount && (hi = hList[hIndex]) && crashlog_header_name(hi); \ hIndex++ ... // Load the classificationfor(EACH_HEADER) {// Get the list of categories in the order that post-compiled categories come before (attachCategories are the order of traversal of the list of categories, // combine the behavior of the attachList with the priority of calls to methods of the same name defined by different categories of the class, (category_t **catlist = _getObjc2CategoryList(hi, &count);for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
if(! CLS) {// Discard all information about the class if it is empty catlist[I] = nil;continue; } // First add the class to the unprocessed class // then handle the unprocessed class by class // reform the class list of properties, method list // Class handle bool classExists = NO;if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if(CLS ->isRealized()) {// realize 'class_ro_t' in class isRealized(CLS); // configure 'class_rw_t' in remethodizeClass(CLS); classExists = YES; }} // The metaclassif (cat->classMethods || cat->protocols
/* || cat->classProperties */)
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
ts.log("IMAGE TIMES: discover categories"); . }Copy the code
2.1 addUnattachedCategoryForClass
Classes that have not yet added the method list, attribute list, and other information to the class_rw_t of the extended class are stored in a static NXMapTable class hash table. The hash table takes Class as the keyword and category_list as the value. The hash table is obtained through unattachedCategories(). (category_list *)NXMapGet(unattachedCategories(), CLS) retrieves the list of unprocessed categories for a class CLS and adds it to the end of the unprocessed categories list for the class when an unprocessed category needs to be added to CLS.
static void addUnattachedCategoryForClass(category_t *cat, Class cls, header_info *catHeader) { runtimeLock.assertWriting(); Cat -> CLS ->isa NXMapTable * CATS = unattachedCategories(); cat-> CLS ->isa NXMapTable * CATS = unattachedCategories(); category_list *list; list = (category_list *)NXMapGet(cats, cls);if(! list) { list = (category_list *) calloc(sizeof(*list) + sizeof(list->list[0]), 1); }else{ list = (category_list *) realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1)); } // add category to the end of list list->list[list->count++] = (locstamped_category_t){cat, catHeader}; NXMapInsert(cats, cls, list); } static NXMapTable *unattachedCategories(void) { runtimeLock.assertWriting(); static NXMapTable *category_map = nil;if (category_map) return category_map;
category_map = NXCreateMapTable(NXPtrValueMapPrototype, 16);
return category_map;
}
Copy the code
2.2 remethodizeClass
RemethodizeClass is used to process all categories in the unattachedCategories hash table generated in 2.1, adding the methods, attributes, and protocols extended by the categories to the method list, attribute list, and protocol list of the class one by one. Class method list in a two-dimensional array container with a compiled method_list_t first and a compiled method_list_t second. A query for a method in a list of methods is returned by iterating from beginning to end to find the first matching method. Therefore, when responding to methods, the post-compiled method of the classification takes precedence over the first compiled method of the same name.
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
isMeta = cls->isMetaClass();
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
attachCategories(cls, cats, true/*flush caches*/); free(cats); }} / / the Class list of untreated classification launched from a hash table static category_list * unattachedCategoriesForClass (Class CLS, bool realizing) { runtimeLock.assertWriting();return (category_list *)NXMapRemove(unattachedCategories(), cls);
}
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if(! cats)return;
if (PrintReplacedMethods) printReplacements(cls, cats); bool isMeta = cls->isMetaClass(); method_list_t **mlists = (method_list_t **) malloc(cats->count * sizeof(*mlists)); property_list_t **proplists = (property_list_t **) malloc(cats->count * sizeof(*proplists)); protocol_list_t **protolists = (protocol_list_t **) malloc(cats->count * sizeof(*protolists)); // Note that the class list is traversed backwards, so the order of the elements in the method list is the same as that of cats. int mcount = 0; int propcount = 0; int protocount = 0; int i = cats->count; bool fromBundle = NO;while (i--) {
auto& entry = cats->list[i];
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
property_list_t *proplist = entry.cat->propertiesForMeta(isMeta);
if (proplist) {
proplists[propcount++] = proplist;
}
protocol_list_t *protolist = entry.cat->protocols;
if(protolist) { protolists[protocount++] = protolist; } } auto rw = cls->data(); PrepareMethodLists (CLS, mLists, McOunt, NO, fromBundle); rw->methods.attachLists(mlists, mcount); free(mlists);if(flush_caches && mcount > 0) flushCaches(cls); // Add the categorized property list to class rw-> property.attachLists (proplists, propcount); free(proplists); // Add the classified protocol list to class rw->protocols. AttachLists (protolists, protocount); free(protolists); }Copy the code
Third, summary
-
During the application load phase, the categories defined in the code are encapsulated into a category_T structure and all category information is collected into the unattachedCategories hash table, which is used to record the categories that have not yet been added to the class_rw_T of the extended class with the method list, attribute list, and so on. The hash table takes the Class extended by the category as the keyword and the category list composed of all the categories that extend that Class as the value. The category list data structure is category_list structure.
-
After collecting the unattachedCategories hash table, add the method list, attribute list, and protocol list of all categories in unattachedCategories to the class_rw_T of the extended class.
-
Methods of the post-compiled class take precedence over methods of the same name of the first compiled class. That is, in the class’s method list two-dimensional array container, the compiled class method_list_t comes first, the compiled class method_list_t comes second, and the class’s base method list comes last;
-
The next article introduces some of the details of runtime’s object-oriented implementation;