This article, the eleventh in the Objective-C series, covers the features and underlying implementations of the load and Initialize special methods.

  • Objective C (7) Object memory analysis

  • Objective C (8) The nature and classification of objects

  • Objective-c (9) KVC and KVO

  • Objective-c (10) Category

  • Objective-c load and Initialize

  • Objective-c (12) Associative objects

First, common knowledge points

Before we talk about it, let’s first list some common knowledge points of the two methods.

Second, plus the load

2.1 Source Guide

Read the objC source code in the following order.

2.2 Test Cases

The test case source code is in **02-load-initialize**

2.2.1 Classes and superclasses are included+load

  • Call the parent class first+loadmethods
  • Call the class+loadmethods
@implementation BFPerson
+ (void)load
{
    NSLog(@"+[BFPerson load]");
}
@end

@implementation BFBoy
+ (void)load
{
    NSLog(@"+[BFBoy load]");
}
@end
Copy the code

Output log:

14:21:44. 542004 + 0800 Category [4483-5050573] + [BFPerson load]

14:21:44. 542545 + 0800 Category [4483-5050573] + [BFBoy load]

2.2.2 classification haveloadmethods

  • Call the class first+loadmethods
  • And then call classified+loadmethods
    • Classification of+loadThe method call order is the same as the compile order
@implementation BFBoy
+ (void)load
{    NSLog(@"+[BFBoy load]"); }@end

@implementation BFBoy (Handsome)
+ (void)load
{  NSLog(@"+[BFBoy load]--Handsome Cat"); }@end


@implementation BFBoy (Tall)
+ (void)load
{	NSLog(@"+[BFBoy load]--Tall Cat"); }
@end
Copy the code

Output log:

14:24:09. 679907 + 0800 Category [4565-5054709] + [BFPerson load]

14:24:09. 680555 + 0800 Category [4565-5054709] + [BFBoy load]

14:24:09.680632+0800 Category[4565:5054709] +[BFPerson load]–Wrok Cat

14:24:09.680749+0800 Category[4565:5054709] +[BFBoy load]–Tall Cat

14:24:09.680845+0800 Category[4565:5054709] +[BFBoy load]–Handsome Cat

2.3 Core Process

2.3.1 Extraction +load method

  • (1) Extract the +load method of the class
    • A. First extract the +load method of the parent class
    • B. Extract the subclass’s +load method
  • (2) The +load method of extracting classification
void prepare_load_methods(const headerType *mhdr)
{
    //1. Get a list of non-lazy-loaded classes
    classref_t *classlist = _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
        Load --> loadable_classes for the class and its parent
        schedule_class_load(remapClass(classlist[i]));
    }

    //2. Get the category list
    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        // 2. Load --> loadable_categoriesadd_category_to_loadable_list(cat); }}Copy the code

2.3.2 Calling the +load method

Call load (loadable_classes and loadable_categories) one by one.

  • (1) Call the class (including the parent class) first+loadmethods
    • A. Call the parent class first+loadmethods
    • B. Call the subclass again+loadmethods
  • (2) call classified+loadmethods
void call_load_methods(void)
{
    do {
        // 1. The load method of the class will be iterated through first
        while (loadable_classes_used > 0) {
            call_class_loads();
        }
        // 2. Call the load method of all classes in turn
        more_categories = call_category_loads();
    } while (loadable_classes_used > 0  ||  more_categories);
}
Copy the code

2.4. Source code interception

2.4.1 Extract class +load method

void add_class_to_loadable_list(Class cls)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = cls->getLoadMethod();  // Get the IMP of the load in the class
    if(! method)return;  // Don't bother if cls has no +load method
    
    if (PrintLoading) {
        _objc_inform("LOAD: class '%s' scheduled for +load", 
                     cls->nameForLogging());
    }
    
    Struct loadable_class {CLS; struct loadable_class {CLS; // may be nil IMP method; }; 2. Loadable_classes stores the loadable_class list 3. Loadable_classes_used Current load stores the loadable_class number 4 loadable_classes_allocated*2 + 16; 4.1 realloc void * realloc (void * PTR, size_t size) to readjust malloc or calloc before the assigned the PTR points to the size of the memory block. * /

    if (loadable_classes_used == loadable_classes_allocated) {
        loadable_classes_allocated = loadable_classes_allocated*2 + 16;
        loadable_classes = (struct loadable_class *)
            realloc(loadable_classes,
                              loadable_classes_allocated *
                              sizeof(struct loadable_class));
    }
    
    loadable_classes[loadable_classes_used].cls = cls;
    loadable_classes[loadable_classes_used].method = method;
    loadable_classes_used++;
}
Copy the code

2.4.2 Classification method extraction

void add_category_to_loadable_list(Category cat)
{
    IMP method;

    loadMethodLock.assertLocked();

    method = _category_getLoadMethod(cat);

    // Don't bother if cat has no +load method
    if(! method)return;

    if (PrintLoading) {
        _objc_inform("LOAD: category '%s(%s)' scheduled for +load", 
                     _category_getClassName(cat), _category_getName(cat));
    }
    Struct loadable_category {Category cat; struct loadable_category {Category cat; // may be nil IMP method; }; "Loadable_categories" stores the "loadable_category" list. "loadable_categories_used" Current load applies to "loadable_category". 4.1 RealLOc void * realLOc (void * PTR, Size_t size) resize the block of memory pointed to by PTR allocated by previous calls to malloc or calloc. 4.2 Allocated loadable_categories_Tokens *2 + 16; That is, after the last allocation, it can be used for (2 times +16) times before it is re-allocated */
    if (loadable_categories_used == loadable_categories_allocated) {
        loadable_categories_allocated = loadable_categories_allocated*2 + 16;
        loadable_categories = (struct loadable_category *)
            realloc(loadable_categories,
                              loadable_categories_allocated *
                              sizeof(struct loadable_category));
    }

    loadable_categories[loadable_categories_used].cat = cat;
    loadable_categories[loadable_categories_used].method = method;
    loadable_categories_used++;
}
Copy the code

2.4.3 + Load method call

(*load_method)(cls, SEL_load);
Copy the code

Three, plus the initialize

3.1 Source Guide

Since +initialize is called when the class first receives a message, we call **[BFPerson alloc]; Is called when **.

We got the following flow from assembly Debug:

> 1. objc_msgSend > 2. objc_msgSend_uncached > 3._class_lookupMethodAndLoadCache3 > 4. lookUpImpOrForward > 5. _class_initialize 5.1 > > _setThisThreadIsInitializingClass 5.1.1 (objc_class *) > > > _fetchInitializingClassList 5.2 > > CALLING_SOME+initialize_METHOD: 5.2.1 > > > objc_msgSend (CLS, SEL_initialize) / / in this step will print + [BFPerson initialize] - Study the Cat 5.3 > > lockAndFinishInitializingCopy the code

Next, we go to objC4 source mining.

We found that 1, 2 in the source code are assembly, 3 is readable C code. Let’s start at 3.

3.2 Test cases

The test case source code is in **02-load-initialize**

3.2.1 Subclass overwrite **+initialize**

  • Call the parent class firstinitialize
@implementation BFPerson
+ (void)initialize
{
    NSLog(@"+[BFPerson initialize]");
}
@end

@implementation BFBoy
+ (void)initialize
{
    NSLog(@"+[BFBoy initialize]");
}
@end
Copy the code

call

[BFBoy alloc];
Copy the code

Print the following:

13:44:35. 437131 + 0800 Category [3527-4997267] + [BFPerson initialize]

13:44:35. 437240 + 0800 Category [3527-4997267] + [BFBoy initialize]

**+initialize**

  • Class will be called+initialize, does not call the class itself+initialize
@implementation BFPerson
+ (void)initialize
{
    NSLog(@"+[BFPerson initialize]");
}
@end

@implementation BFPerson (Work)
+ (void)initialize
{
    NSLog(@"+[BFBoy initialize]");
}
@end
Copy the code

Call:

[BFBoy alloc];
Copy the code

A printout

13:45:35.437121+0800 Category[3527:4997267] +[BFPerson initialize]–Wrok Cat

3.2.3 Multiple callsinitialize

None of the subclasses are implemented, but the parent class implements the +initialize method, which will be called multiple times.

@implementation BFPerson
+ (void)initialize
{
    NSLog(@"+[BFPerson initialize]");
}
@end

@implementation BFBoy
@end

@implementation BFGirl
@end
Copy the code

Call:

[BFBoy alloc];
[BFGirl alloc];
Copy the code

A printout

13:52:44. 968194 + 0800 Category [3786-5010745] + [BFPerson initialize]

13:52:44. 968327 + 0800 Category [3786-5010745] + [BFPerson initialize]

13:52:44. 968427 + 0800 Category [3786-5010745] + [BFPerson initialize]

3.3. Core processes

Class initialization calls the parent +initialize method and the class itself +initialize.

/ / pseudo code
BOOL personInitialized = NO;
BOOL boyInitialized = NO;

if(! boyInitialized) {//1. Superclass call
    if(! personInitialized) { objc_msgSend([BFPersonclass].@selector(initialize));
        personInitialized = YES;
    }
    //2. The class itself calls
	objc_msgSend([BFBoy class].@selector(initialize));
	boyInitialized = YES;
}
Copy the code

3.4. Source code interception

3.4.1 Finding methods

IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    ....
	// Needs to be initialized, and the class is not initialized
    if(initialize && ! cls->isInitialized()) {//initialize_class_initialize (_class_getNonMetaClass(cls, inst)); }... }Copy the code

3.4.2 class+initializecall

void _class_initialize(Class cls)
{
    // 1. Call the parent class initialization
    if(supercls && ! supercls->isInitialized()) { _class_initialize(supercls); }...//2. Send an Initialize message to the classcallInitialize(cls); .//3. Complete class initializationlockAndFinishInitializing(cls, supercls); . }Copy the code

reference

link

  1. Objc source

The sample code

  1. 02-load-initialize