Basic use of Category

Classes, like classes, are declared in interfaces and implemented in class files. But classes cannot declare instance variables, only properties and methods, and the implementation part of a class cannot contain the @synthesize. When a classification interface contains attribute declarations, the implementation part manually defines the access methods for the attributes. This is to prevent random access to instance variables defined in different files of the same class.

Methods can be instance methods or class methods.

Syntax: Category declaration

Note: The class name must be an existing class. You cannot define a non-existing class.

@interface Class name (class name)

Declaration of attributes

Method declaration

@end

Syntax: Category implementation

@implementation Class name

Setter definition for property

Getter definition for property

Method definition

@end

Interface classification follows the following principles:

1. The interface part of the classification must refer to the interface file of the class to which it belongs.

2. The classification implementation part must refer to the corresponding interface file;

3. When using a method in a classification, you must refer to the header file in which the method resides.

Example: MyModel. H

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MyModel : NSObject
@property (nonatomic.copy)NSString *name;
@property (nonatomic.assign)NSInteger age;
@property (nonatomic.assign)CGFloat height;
- (void)work;
@end

NS_ASSUME_NONNULL_END  
Copy the code

MyModel.m

#import "MyModel.h"

@implementation MyModel

- (void)work{
    NSLog(@"-------MyModel work---------");
}

@end

Copy the code

MyModel+Test.h

#import "MyModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface MyModel (Test)
- (void)walk;
@end

NS_ASSUME_NONNULL_END
Copy the code

MyModel+Test.m


#import "MyModel+Test.h"
@implementation MyModel (Test)
  
- (void)walk{
    NSLog(@"----Test walk--------");
}
@end

Copy the code

Append classes to existing classes

Append new methods

New methods can be added to existing classes, whether custom or systematic.

While classes cannot append instance variables, newly appended methods can access properties and methods in the class. This allows us to add new functionality to existing classes.

When we are having trouble using inheritance to add new functionality to existing classes, we can use classification to add new functionality to existing classes.

Example: Add a new method to the system’s NSDate class that gets the current time and returns a time string in a specified format.

NSDate+Format.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSDate (Format)+ (NSString *)getCurrentDayTimeDetail;
@end

NS_ASSUME_NONNULL_END

Copy the code

NSDate+Format.m

#import "NSDate+Format.h" @implementation NSDate (Format) +(NSString *)getCurrentDayTimeDetail { NSDate *now = [self date]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@" YYYY yyyy MM dd HH: MM :ss"]; [formatter setLocale:[[NSLocale alloc]initWithLocaleIdentifier:@"zh_CN"]]; NSTimeZone *timeZone = [[NSTimeZone alloc]initWithName:@"Asia/Shanghai"]; [formatter setTimeZone:timeZone]; NSString *current = [formatter stringFromDate:now]; return current; } @endCopy the code

Overrides existing methods

If a method in a newly defined classification has the same name as the old method, the new method will override the old method. In this way, method override can be implemented without inheritance. However, if the original method is not covered carefully, it will also cause unpredictable problems, especially if the more important method is covered, serious problems are likely to occur. If there are multiple methods with duplicate names, there is no way to know exactly which method is being executed. Overrides do not warn, so you must be careful not to overwrite methods when using them.

Associated reference

Classification allows us to append new methods to a class, but not instance variables. However, with the runtime functionality of Objective-C, you can add instance variables to an already existing instance object. In this way, classes can be dynamically extended without subclassing. Associated references can be used at run time to not only add associations to objects as needed, but also to remove the added associations.

Adding associated

 void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy);
Copy the code

Object: The owning class, that is, the associated object to be added.

Key: the keyword used to distinguish associated references. The key value must be a fixed address that does not change.

Value: reference object.

Policy: Specifies the storage policy associated with the reference.

This method removes the key association by setting value to nil.

Retrieve the associated

 id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key);
Copy the code

The associated object object is retrieved by the keyword key, and nil is returned if no object is associated.

Remove the associated

The objective-C runtime removes associations as follows. This method removes all associations of an object. Use caution.

Existing code may already use associations, so this method is not recommended. Instead, remove associations separately by using objc_setAssociatedObject and setting the argument to nil.

void objc_removeAssociatedObjects(id _Nonnull object);
Copy the code

Related policies

OBJC_ASSOCIATION_ASSIGN:

When managing memory, the associated object is not sent a retain message and is only associated with an assignment. A weak reference.

OBJC_ASSOCIATION_RETAIN_NONATOMIC:

When managing memory, a retain message is sent to the associated object and holds, and a release message is sent to other objects if the same key is already associated with them. When the owner of an associated object is released, a Release message is sent to all associated objects. Strong reference.

OBJC_ASSOCIATION_COPY_NONATOMIC:

During object association reference, a copy of the original object is copied and the newly copied object is used for association operations.

OBJC_ASSOCIATION_RETAIN:

It is the same as OBJC_ASSOCIATION_RETAIN_NONATOMIC in terms of object holding, except that OBJC_ASSOCIATION_RETAIN is multithread safe and supports exclusive association operations. Objc_getAssociatedObject operates the same as OBJC_ASSOCIATION_RETAIN.

OBJC_ASSOCIATION_COPY:

Object ownership is the same as OBJC_ASSOCIATION_COPY_NONATOMIC, except that OBJC_ASSOCIATION_COPY is multithread safe and supports exclusive association operations.

Example: MyModel + Test. H

#import "MyModel.h"

NS_ASSUME_NONNULL_BEGIN
@protocol MyModelProtocol <NSObject>

- (void)myModelProtocolAction;

@end
  
@interface MyModel (Test) <MyModelProtocol>
  // Instance variables are not automatically generated. Adding properties here is actually adding setter and getter methods.
@property (nonatomic.copy)NSString *email;
- (void)walk;
@end

NS_ASSUME_NONNULL_END

Copy the code

MyModel+Test.m

#import "MyModel+Test.h"
#import <objc/runtime.h>
@implementation MyModel (Test)

- (void)setEmail:(NSString *)email{
    objc_setAssociatedObject(self.@"email",email, OBJC_ASSOCIATION_COPY);// Add the association
// objc_setAssociatedObject(self, @"email",nil, OBJC_ASSOCIATION_COPY); // Remove the association
}

- (NSString *)email{
    // Retrieve the association
    return  objc_getAssociatedObject(self.@"email"); } + (void)load{
    NSLog(@"----Test load-------");
}

//+ (void)initialize{
// NSLog(@"----Test initialize-------");
/ /}
//

- (void)walk{
    NSLog(@"----Test walk-------%@".self.email);
}

- (void)myModelProtocolAction{
    NSLog(@"----myModelProtocolAction-------");
}


@end

Copy the code

The underlying structure

Generate a. CPP file using the terminal command clang-rewrite-objc MyModel+ test. m.

Open the overwritten. CPP file, look for _category_t, and find the following structure at the end of the file.

struct _category_t { const char *name; Struct _class_t * CLS; // class const struct _method_list_t *instance_methods; // list of object methods const struct _method_list_t *class_methods; Const struct _protocol_list_t *protocols; // const struct _prop_list_t *properties; // Attribute list};Copy the code

Object method list structure that stores all object methods in a class

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[4];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	4{{(struct objc_selector *)"setEmail:"."v24@0:8@16", (void *)_I_MyModel_Test_setEmail_},
	{(struct objc_selector *)"email"."@ @ 0:8 16", (void *)_I_MyModel_Test_email},
	{(struct objc_selector *)"walk"."v16@0:8", (void *)_I_MyModel_Test_walk},
	{(struct objc_selector *)"myModelProtocolAction"."v16@0:8", (void *)_I_MyModel_Test_myModelProtocolAction}}
};

Copy the code

The class method structure, which stores the methods of the class

static struct /*_method_list_t*/ {
	unsigned int entsize;  // sizeof(struct _objc_method)
	unsigned int method_count;
	struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_objc_method),
	1{{(struct objc_selector *)"load"."v16@0:8", (void *)_C_MyModel_Test_load}}
};

Copy the code

Note that the **_ivar_list_t member variable structure and setter and getter methods are not found in the compiled file as they are in the class file, which proves that the class cannot add member variables. The setter and getter** methods for properties, compiled, go through the associated reference methods that we add manually.

/ / agreement
static struct /*_protocol_list_t*/ {
	long protocol_count;  // Note, this is 32/64 bit
	struct _protocol_t *super_protocols[1];
} _OBJC_CATEGORY_PROTOCOLS_$_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	1,
	&_OBJC_PROTOCOL_MyModelProtocol
};

/ / property
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_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
	sizeof(_prop_t),
	1,
	{{"email"."T@\"NSString\",C,N"}}};// Add an associated reference
static void _I_MyModel_Test_setEmail_(MyModel * self, SEL _cmd, NSString * _Nonnull email) {
    objc_setAssociatedObject(self, (NSString *)&__NSConstantStringImpl__var_folders_lc_jzfd703x2r1czsk56zjspmw40000gn_T_MyModel_Test_00509d_mi_0,email, OBJC_ASSOCIATION_COPY);

}

// Retrieve the associated reference
static NSString * _Nonnull _I_MyModel_Test_email(MyModel * self, SEL _cmd) {

    return objc_getAssociatedObject(self, (NSString *)&__NSConstantStringImpl__var_folders_lc_jzfd703x2r1czsk56zjspmw40000gn_T_MyModel_Test_00509d_mi_1);
}


Copy the code

The following is the structure assignment of MyModel classification, and the corresponding assignment of each variable is analyzed in turn.

The first is the class name, which assigns MyModel to name;

The second CLS assigns 0 to CLS;

Set _OBJC$CATEGORY_INSTANCE_METHODS_MyModel$_Test to _instance_methods;

*_OBJC$CATEGORY_CLASS_METHODS_MyModel$_Test = class_methods;

In the fifth protocol list, the _OBJC_CATEGORY_PROTOCOLS$MyModel$_Test variable is assigned to protocols;

Finally, the property list assignment assigns the _OBJC_$_PROP_LIST_MyModel_$_Test variable to properties;

static struct _category_t _OBJC_$_CATEGORY_MyModel_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"MyModel".0.// &OBJC_CLASS_$_MyModel,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_MyModel_$_Test,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_MyModel_$_Test,
	(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_MyModel_$_Test,
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MyModel_$_Test,
};
Copy the code

From the compiled code analysis above, we can see that the compiler generates a _category_t structure and assigns values to variables defined within the structure. The _category_t structure stores classified object methods, class methods, protocols, and properties, but has no member variables. This is the underlying structure of the _category_t rewrite.

Source code analysis

The Category runtime loading process

_objc_init->map_images->map_images_nolock->_read_images->realizeClassWithoutSwift->methodizeClass->attachToClass

In the above execution, the map_images method is executed only once, which loads and caches all the Mach-O images and does nothing to merge and categorize them.

load_images->loadAllCategories->load_categories_nolock->attachCategories->attachLists

During this process, the classification is formally merged. Note that load_images is executed only after map_images is executed, multiple times. But load_images loadAllCategories method, only in the classification haven’t initialization and _dyld_objc_notify_register call has been completed, namely didInitialAttachCategories to false, DidCallDyldNotifyRegister to true to enable at this time, this time will no longer go realizeClassWithoutSwift – > methodizeClass – > attachToClass this part, because the class object has been around for, So the process behind loadAllCategories has been changed.

_objc_init

In the objC (OBJC4-818.2) open source library, the _objc_init method is found in the objC-os. mm class file. In this method, the _dyLD_OBJC_notify_register function registers three methods. Let’s focus on the first two important function arguments;

Map_images: Loads and caches all mach-O image files;

Load_images: required when initializing classes and categories;

Unmap_image: cancels memory mapping.

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_init();
    exception_init();
#if __OBJC2__
    cache_t::init();
#endif
    _imp_implementationWithBlock_init();
     //
    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

#if __OBJC2__
    didCallDyldNotifyRegister = true;
#endif
}

Copy the code

map_images

Load and cache all Mach-O images in map_images_NOLock. The program is executed only once it is started.

void
map_images(unsigned count, const char * const paths[],
           const struct mach_header * const mhdrs[])
{
    mutex_locker_t lock(runtimeLock);
    return map_images_nolock(count, paths, mhdrs);
}

Copy the code

map_images_nolock

Function: addHeader is executed for all mirrors to filter duplicate mirrors. This method calls the _read_images method that loads all the Mach-O image files.

void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                  const struct mach_header * const mhdrs[])
{
     /** * the code is too long

    if (hCount > 0) { _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); }}Copy the code

_read_images

During the map_images method call, the _read_images method in objC-Runtime-new. mm file is eventually referenced. The _read_images method is used to read all class information, method information, and classification information from the Mach-O image file.

void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
{
    /** ** ** /
 
    // Search for categories
    if (didInitialAttachCategories) {
        for(EACH_HEADER) { load_categories_nolock(hi); }}/** * This thread completes its repair before another thread calls the new class code. Category searches must be delayed to avoid potential competition. * /
    for (EACH_HEADER) {
        classref_t const *classlist = hi->nlclslist(&count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if(! cls)continue;

            addClassTableEntry(cls);

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy", cls->nameForLogging()); }}//// performs the first initialization of class CLS, excluding swift initialization
            realizeClassWithoutSwift(cls, nil); }}}Copy the code

realizeClassWithoutSwift

What it does: Implements ClassWithoutSwift, performs the first initialization of the CLS class, including assigning its read and write data. No Swift side initialization is performed.

Returns the true class structure of the class. Locking: The runtimeLock read-write lock must be written on the caller to keep the thread safe.


static Class realizeClassWithoutSwift(Class cls, Class previously)
{
   runtimeLock.assertLocked();
    /** ** ** /
  // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }
     // Add class
    // Attach categories
    methodizeClass(cls, previously);

    return cls;
}

Copy the code

methodizeClass

Effect: Modify CLS method list, protocol list, and property list. Attach all classes of the CLS class that are not attached to the CLS. Add methods, attributes, and protocols from a classification to methods, Properties, and Protocols. Locking: The runtimeLock read-write lock must be written on the caller to keep the thread safe.

static void methodizeClass(Class cls, Class previously)
{
    /** ** ** /
 
    runtimeLock.assertLocked();
  
    // Install methods and properties that the class implements itself.
  // Add methods and attributes implemented by the class itself
    method_list_t *list = ro->baseMethods();
    if (list) {
        prepareMethodLists(cls, &list, 1.YES, isBundleClass(cls), nullptr);
        if (rwe) rwe->methods.attachLists(&list, 1);
    }

    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 it is the root metaclass
    if (cls->isRootMetaclass()) {
        // root metaclass
     // Swap the initialize method of the root metaclass, sending SEL_initialize to the root metaclass, but using the objC_noop_IMP method, which does nothing
        addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "".NO);
    }

    // Attach categories.
    // Add a class to the CLS class
    if (previously) {// If the class already exists
        if (isMeta) {// If it is a metaclass, append the CLS classification and return the unattached CLS class
            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.
          // When a class is repositioned, classes with class methods can be registered in the metaclass on the class itself instead of on the class. Add these via attachToClass.
            objc::unattachedCategories.attachToClass(cls, previously,
                                                     ATTACH_CLASS_AND_METACLASS);
        }
    }
    objc::unattachedCategories.attachToClass(cls, cls,
                                             isMeta ? ATTACH_METACLASS : ATTACH_CLASS);

}

Copy the code

load_images

Action: The load_images method is called back when the state of the image changes. Enabled only after map_images ends and classification is not initialized. It will be called multiple times.

extern bool hasLoadMethods(const headerType *mhdr);
extern void prepare_load_methods(const headerType *mhdr);

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.
    if(! hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    call_load_methods();
}

Copy the code

loadAllCategories

What it does: Loads an entry for all categories

static void loadAllCategories() {
    mutex_locker_t lock(runtimeLock);

    for(auto *hi = FirstHeader; hi ! =NULL; hi = hi->getNext()) { load_categories_nolock(hi); }}Copy the code

load_categories_nolock

Function: Load the distinction judgment before classification.

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 || If (CLS ->isRealized()) {attachCategories(CLS, &lc, 1) ATTACH_EXISTING); } else { objc::unattachedCategories.addForClass(lc, cls); } } if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) { if (cls->ISA()->isRealized()) { attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS); } else { objc::unattachedCategories.addForClass(lc, cls->ISA()); }}}}}; processCatlist(hi->catlist(&count)); processCatlist(hi->catlist2(&count)); }Copy the code

attachCategories

What it does: Add method lists, attributes, and protocols from the category list to the CLS class. AttachCategories require that the category list be sorted, with the new one first.

static void attachCategories(Class cls, const locstamped_category_t *cats_list, Uint32_t cats_count,int flags) {/** * 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; Bool isMeta = (flags & ATTACH_METACLASS); 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) {// Prepare a list of methods in mlists, McOunt: number of lists, NO: exclude basic methods, fromBundle: Whether fromBundle prepareMethodLists(CLS, mlists, McOunt, NO, fromBundle, __func__); Rwe ->methods.attachLists(mlists, McOunt); mcount = 0; } mlists[ATTACH_BUFSIZ - ++mcount] = mlist; fromBundle |= entry.hi->isBundle(); } // property_list_t *proplist = entry.cat->propertiesForMeta(isMeta, entry.hi); If (proplist) {if (propCount == ATTACH_BUFSIZ) {// Add the new Proplist list to the property list array in RWE 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) {// Add the new protolist list to the protocol list array in RWE rwe->protocols.attachLists(protolists, protocount); protocount = 0; } protolists[ATTACH_BUFSIZ - ++protocount] = protolist; } } if (mcount > 0) { prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle, __func__); rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount); if (flags & ATTACH_EXISTING) { 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->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount); rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount); }Copy the code

attachLists

Merges the new list data with the existing list data.

  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(); }}void tryFree() {
        if (hasArray()) {
            for (uint32_t i = 0; i < array()->count; i++) {
                try_free(array()->lists[i]);
            }
            try_free(array());
        }
        else if (list) {
            try_free(list);
        }
    }

    template<typename Other>
    void duplicateInto(Other &other) {
        if (hasArray()) {
            array_t *a = array();
            other.setArray((array_t *)memdup(a, a->byteSize()));
            for (uint32_t i = 0; i < a->count; i++) { other.array()->lists[i] = a->lists[i]->duplicate(); }}else if (list) {
            other.list = list->duplicate();
        } else {
            other.list = nil; }}};Copy the code

The Category of the load

Load is called first if the class is compiled first.

2. Call load from the parent class first, then call load from the child class.

Class load is called first, then the class load is called, then the compiled class load is called first;

4, call time is the most early, before the main function runs, load method will be called, suitable for method exchange.

5. The load method is not lazy. It is only called once during a program call.

The Category of the initialize

Initialize (initialize); initialize (initialize); initialize (initialize);

Initialize is called when a class receives a message for the first time. Each class is initialize only once, and its parent may be called more than once.

Unlike load, when the Initialize method is called, all classes are loaded into memory.

Initialize is thread safe. Constants are only initialized in initialize.

Conclusion:

The Category runtime loads classes and metaclasses by dynamically initializing information about classes and metaclasses, and then merging classified instance methods, class methods, attributes, and protocols into class objects and metaclasses.