Category is a language feature added after Objective-C 2.0. The main purpose of a category is to add methods to existing classes.

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);
};
Copy the code

Analyze the category loading process from source code

Create a new Person class and write a new Person class Person+test that implements the parent class’s methods

Person *p = [Person new];
 [p run];
//[Person(test) run]
Copy the code

The final output of the classification method, the classification method overrides the original class method?

- (void)printMethodOfClass:(Class)class{ unsigned int count ; /** get Method array */ Method *methodList = class_copyMethodList(class, &count); /** NSMutableString */ NSMutableString *mothodNames = [NSMutableString string]; for (int i=0 ; i<count ; I ++) {/** get Method */ Method = methodList[I]; / / NSString *methodName = NSStringFromSelector(method_getName(method)); [mothodNames appendString:methodName]; [mothodNames appendString:@", "]; } free(methodList); NSLog(@"%@ - %@", class, mothodNames); } person-red, red, run, run,Copy the code

The output of all the current methods does not overwrite the methods of the original class, so we analyze the source code of objC to see how the classified methods are loaded

  1. Let’s start with objc_init
Void _objc_init(void) {dyLD_objc_notify_register (&map_images, load_images, unmap_image); }Copy the code

Load_images: The executable file will be in producet after the app is compiled. Through Mach-O analysis, it can be found that Function Starts has been loaded into map through MAP_images

  1. _getObjc2CategoryList 2.1 remethodizeClass 2.2 attachCategories(Class CLS, category_list *cats, Bool flush_caches) 2.2.1 Preparation: method_list_t property_list_t protocol_list_t 2.2.2 Operation
    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    rw->protocols.attachLists(protolists, protocount);
    free(protolists);

Copy the code

2.2.3

Next, in attachLists RW,

prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);
Copy the code

And then the original array starts to change,

  1. Array expansion: newCount = oldCount + addedCount changes the number of arrays, followed by realloc, the original array becomes the new array
  2. Move: Move the sorted array to the start of the original array in Lists [0] copy: Copy the assembled array in one step
uint32_t oldCount = array()->count; uint32_t newCount = oldCount + addedCount; setArray((array_t *)realloc(array(), array_t::byteSize(newCount))); array()->count = newCount; // Copy memmove(array()-> Lists + addedCount, array()->lists, oldCount * sizeof(array()->lists[0])); memcpy(array()->lists, addedLists, addedCount * sizeof(array()->lists[0]));Copy the code

[p run] [p run] [P run] [P run] [P run] [P run] [P run] [P run] [P run] [P run

  1. The method for the classes is added before the corresponding method of the class and not all of the classes are in front of it, because of complexity, because once the classes are inserted first, all of the subscripts in the methodList have to be changed, but when you insert the star of the class before the corresponding method, In this way, the subscript of the methodList preceding the method of this class does not need to be moved.
  2. The order in which Category calls are compiled before this class is determined by the order in which xcode ->Build Phases->Compile Sources files are compiled: Category > This class > parent class. Methods in Cateory are called first, then methods of its own class, and finally methods of its parent class.

For detailed analysis, see the +load method parsing

• You can’t add member variables to a category. Why

`struct objc_class {` `Class isa OBJC_ISA_AVAILABILITY; ` `#if ! __OBJC2__` `Class super_class OBJC2_UNAVAILABLE; // Parent class const char *name OBJC2_UNAVAILABLE; '// Class name' 'long version OBJC2_UNAVAILABLE; // Class version information, default is 0 long info OBJC2_UNAVAILABLE; '// class information, some bit identifier' 'for run-time use' 'long instance_size OBJC2_UNAVAILABLE; Struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; Struct objc_method_list **methodLists OBJC2_UNAVAILABLE; Struct objc_cache *cache OBJC2_UNAVAILABLE; Struct objc_PROTOCOL_list *protocols OBJC2_UNAVAILABLE; '// Protocol linked list' '#endif' '} OBJC2_UNAVAILABLE; `Copy the code

Ivars is a pointer to objc_iVAR_list (member variable list); MethodLists are Pointers to the objc_method_list pointer. In Runtime, the objc_class structure is fixed in size. It is not possible to add data to the structure, only to modify it. Therefore, ivars points to a fixed area and can only change the value of member variables, but cannot increase the number of member variables. MethodList is a two-dimensional array, so you can change the value of *methodLists to add member methods, and while you can’t expand the memory area that methodLists point to, you can change the value of that memory area (which stores Pointers). Therefore, methods can be added dynamically, not member variables. • Categories actually allow you to add attributes, and you can also use @property, but they don’t generate _ variables (underlined member variables), and they don’t generate getters and setters for adding attributes, so despite adding attributes, You can’t use dot syntax to call getter and setter methods. The Runtime is required to dynamically add bindings.

- (void)setName:(NSString *)name {
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)name {
    return objc_getAssociatedObject(self,@selector(name));
}
Copy the code

[Class and classification loading](