In-depth explanation of iOS +load and +initialize

In-depth explanation of iOS +load and +initialize

Before this, I never thought that +load and +initialize can pull so many things, but today it does pull so many things. If there are any mistakes, please correct me~~~

The +load method is automatically called by the system, and there is no need to manually call it. The system automatically calls the +load method (if any) for each class, so there is no need to manually call the [super load] method. The +load method is loaded in the order of [SuperClass load]->[Class load]->[ChildClass load]. The +load method is called after all classes are added to the runtime. The [ChildClass load] method is loaded according to the order of Compile Sources, but the [SuperClass load] method must be called before calling [ChildClass load]. Call the [Category load] method after the +load method of all classes is called. The call sequence of [Category load] is completely in accordance with the order of Compile Sources. For the convenience of reading, I removed all the output time in the console.

At the beginning of learning, first we create a new project LoadAndInitializeTest project

1. The +load method is automatically called by the system, and there is no need to manually call it. The system automatically calls the +load method (if any) for each class, so there is no need to manually call the [super load] method. Create a new folder (object) in Xcode, and then create a subclass MyObject of NSObject. Add the following code to the MyObject.m file, and then run +(void)load {NSLog(@"%s", FUNCTION );} print out

LoadAndInitializeTest[27822:1740708] +[MyObject load] We did not manually call any method of MyObject, but the +load method was indeed called, so the +load method is called automatically by the system, and there is no need to manually call it.

Create a new folder (super) in Xcode, then create a new subclass of NSObject MyObjectSuper, and then change the parent class of MyObject to MyObjectSuper. And enter the following code in MyObjectSuper.m, then run +(void)load {NSLog(@"%s", FUNCTION );} print out

LoadAndInitializeTest[31059:1753828] +[MyObjectSuper load] LoadAndInitializeTest[31059:1753828] +[MyObject load] Obviously, the +load method of the parent class is also automatically loaded, so there is no need to manually call it.

Add [super load] in [MyObject load], and then run print out LoadAndInitializeTest[33168:1761400] +[MyObjectSuper load] LoadAndInitializeTest[33168:1761400] +[MyObjectSuper load] LoadAndInitializeTest[33168:1761400] +[MyObject load] visible [MyObjectSuper load] is called twice, which also shows that the [SuperClass load] method is also automatically loaded, so there is no need to manually call it. For the sake of safety, the uniqueness judgment must be done in +load, generally use the following code.

  • (void)load {static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ doSomething });} Let s do one more test. If a programmer writes a [super load] in [ChildClass load], and [ SuperClass load] s responsibility is to use black magic to exchange methods. [SuperClass load] will be called twice. If the methods are exchanged twice, it means there is no exchange. If you don t understand the use of the +load method, like this bug, we It's hard to find. Add the following code to the parent class (MyObjectSuper) here just to demonstrate the +load method, the pit of black magic will not be explained here in detail

#import "MyObjectSuper.h" #import <objc/runtime.h>

@implementation MyObjectSuper

  • (void)load{ NSLog(@"%s", FUNCTION ); Method method1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc")); Method method2 = class_getInstanceMethod([self class], @selector(deallocSwizzle) ); method_exchangeImplementations(method1, method2);}

-(void)dealloc {NSLog(@"%s", FUNCTION );}

  • (void)deallocSwizzle{ NSLog(@"%s", FUNCTION ); [self deallocSwizzle];}

@end Add the following code to the subclass (MyObject), pay attention to this line of code: [super load];

#import "MyObject.h"

@implementation MyObject

  • (void)load {[super load]; NSLog(@"%s", FUNCTION );} @end We create and destroy objects elsewhere, and then run
  • (void)viewDidLoad {[super viewDidLoad]; [[MyObject alloc] init];} printout

LoadAndInitializeTest[74856:1942900] +[MyObjectSuper load] LoadAndInitializeTest[74856:1942900] +[MyObjectSuper load] LoadAndInitializeTest[74856:1942900] +[MyObject load] LoadAndInitializeTest[74856:1942900] -[MyObjectSuper dealloc] Analysis result: [MyObjectSuper dealloc] ] Is called twice and the method is swapped twice, which is equivalent to no swap, so when the object is destroyed, [MyObjectSuper dealloc] is called instead of [MyObjectSuper deallocSwizzle].

Then we now comment out the [super load]; in the subclass, do not modify the other code, and then run the print output

LoadAndInitializeTest[76655:1950334] +[MyObjectSuper load] LoadAndInitializeTest[76655:1950334] +[MyObject load] LoadAndInitializeTest[76655:1950334] -[MyObjectSuper deallocSwizzle] LoadAndInitializeTest[76655:1950334] -[MyObjectSuper dealloc] Haha~~~~ It's normal. We must know that the +load method may be called multiple times. In the load method, we must make judgments, because there is always a programmer who inherits your class and then calls [super load in the load method. ].

Now our modified code in the parent class is as follows

  • (void)load {static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"%s", FUNCTION ); Method method1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc")); Method method2 = class_getInstanceMethod( [self class], @selector(deallocSwizzle)); method_exchangeImplementations(method1, method2); });} The subclass code is as follows, then run

  • (void)load {[super load]; NSLog(@"%s", FUNCTION );} printout

LoadAndInitializeTest[76655:1950334] +[MyObjectSuper load] LoadAndInitializeTest[76655:1950334] +[MyObject load] LoadAndInitializeTest[76655:1950334] -[MyObjectSuper deallocSwizzle] LoadAndInitializeTest[76655:1950334] -[MyObjectSuper dealloc] This is really normal , We are no longer afraid of [super load] being called in the subclass + load method

When I was reading some well-known third libraries, such as AFNetworking and MJRefresh of the small code brother, I did find some improper practices. Although they are not serious bugs, they are hidden dangers. The code is as follows AFNetworking code, only post one A small part of @implementation _AFURLSessionTaskSwizzling

  • (void)load {if (NSClassFromString(@"NSURLSessionTask")) {I created a subclass of _AFURLSessionTaskSwizzling, re-+load method in the subclass, and then [super load]; found that the program can indeed be transferred here}} Load @implementation UIViewController (Example) of MJRefresh of Little Code
  • (void)load {Method method1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc")); Method method2 = class_getInstanceMethod([self class], @selector(deallocSwizzle)); method_exchangeImplementations(method1, method2);} in This is how it is written in the Didi Travel client. It can be seen that I have already noticed this problem.

@implementation BaseViewController (categroy)

  • (void)load {static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{//method exchange});} 2. The +load method is loaded in the order of [SuperClass load]->[Class load]->[ChildClass load]-> . MyObjectSuper and MyObject comment out the useless information, only keep the following code
  • (void)load {NSLog(@"%s", FUNCTION );} @end We create a new child folder, and create a subclass of MyObject MyObjectChild under the child folder, add the following code, and then run

#import "MyObjectChild.h" @implementation MyObjectChild

  • (void)load {NSLog(@"%s", FUNCTION );} @end printout

LoadAndInitializeTest[9937:2079123] +[MyObjectSuper load] LoadAndInitializeTest[9937:2079123] +[MyObject load] LoadAndInitializeTest[9937:2079123] +[MyObjectChild load] It can be seen that the calling sequence of the +load method starts from the parent class, then the child class, Then subclass, I tried to change the order in Compile Sources, the result is still the same, which proves that the calling order of the +load method is from the parent class to the subclass.

3. The +load method is called after all classes are added to the runtime. Before sharing this question, let s take a look at a short paragraph of Apple s documentation on +load

Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.

For this document, the great god Zhu Xiaohui translated it like this

When the class (Class) or category (Category) is added to the Runtime (that is, when it is referenced). To implement this method, you can do some class-specific operations during loading.

And I translated it like this

When a class or category is added to Runtime, implementing this method can do some class-specific operations during loading.

The fundamental difference between the above two translations is "Shi" and "Later". I think "Shi" refers to the ongoing time and the adding. And "later" is after the Add operation is successful, it is a kind of completion tense. Now let s test what is going on!

Modify MyObject.h as follows to comply with an NSObject protocol, and add two property attributes

@interface MyObject: MyObjectSuper @property (nonatomic, copy) NSString *name; @property (nonatomic, assign) NSInteger age; @end Modify MyObject.m as follows, a bit too much, let s see for yourself

#import <objc/runtime.h>

@implementation MyObject {int nIval;//Increase the first place}

  • (void)load {NSLog(@"%s", FUNCTION );

    //In the second place, add NSLog(@"-1.------Gorgeous style-------"); unsigned int count = 0; Class metaClass = object_getClass([MyObject class]); Method *methods = class_copyMethodList(metaClass, &count); for (int i = 0; i <count; i++) {NSLog(@"Class method is: %s", sel_getName(method_getName(methods[i])));} free(methods);

    NSLog(@"-2.------Gorgeous style------"); unsigned int countMethod = 0; methods = class_copyMethodList([self class], &countMethod); for (int i = 0 ; i <countMethod; i++) {NSLog(@"The instance method is: %s", sel_getName(method_getName(methods[i])));} free(methods);

    NSLog(@"-3.------Gorgeous style-------"); unsigned int countIval = 0; Ivar *ivals = class_copyIvarList([self class], &countIval); for (int i = 0; i <countIval; i++) {NSLog(@"The variable is: %s", ivar_getName(ivals[i]));} free(ivals);

    NSLog(@"-4.------Gorgeous style------"); unsigned int countProperty = 0; objc_property_t *propertys = class_copyPropertyList([self class], &countProperty); for (int i = 0; i <countProperty; i++) {NSLog(@"Property is: %s", property_getName(propertys[i]));} free(propertys);

    NSLog(@"-5.------Gorgeous style ------"); unsigned int countProtocol = 0; __unsafe_unretained Protocol **protocols = class_copyProtocolList([self class], &countProtocol); for ( int i = 0; i <countProtocol; i++) {NSLog(@"The protocol is: %s", protocol_getName(protocols[i]));} NSLog(@"------Gorgeous style--- ---");

    MyObject *myObject = [[MyObject alloc] init]; myObject.age = 18; myObject.name = @" "; NSLog(@"myObject.name=%@,myObject.age=%ld",myObject. name, myObject.age);}

  • (void)objectFunction {//Add NSLog(@"%s", FUNCTION );}
  • (void)classFunction {//In the fourth place, add NSLog(@"%s", FUNCTION );} @end Now you can think more about whether the output in your mind is consistent with the actual output.

LoadAndInitializeTest[33804:2175226] +[MyObjectSuper load] LoadAndInitializeTest[33804:2175226] +[MyObject load] LoadAndInitializeTest[33804:2175226] -1.------ Under gorgeous style------ LoadAndInitializeTest[33804 :2175226] The class method is: classFunction LoadAndInitializeTest[33804:2175226] The class method is: load LoadAndInitializeTest[33804:2175226] -2.-------Gorgeous style ----- LoadAndInitializeTest[33804:2175226] The instance method is: objectFunction LoadAndInitializeTest[33804:2175226] The instance method is: .cxx_destruct LoadAndInitializeTest[33804:2175226] The instance method is: name LoadAndInitializeTest[33804:2175226] The instance method is: setName: LoadAndInitializeTest[33804:2175226] The instance method is: setAge: LoadAndInitializeTest[33804:2175226] The instance method is: age LoadAndInitializeTest[33804:2175226] -3.-------under gorgeous style----- LoadAndInitializeTest[33804:2175226] The variable is: nIval LoadAndInitializeTest[ 33804:2175226] The variable is: _name LoadAndInitializeTest[33804:2175226] The variable is: _age LoadAndInitializeTest[33804:2175226] -4.-------Gorgeous style----- LoadAndInitializeTest[33804:2175226] The property is: name LoadAndInitializeTest[33804:2175226] The property is: age LoadAndInitializeTest[33804:2175226] The property is: hash LoadAndInitializeTest[33804:2175226] The property is: superclass LoadAndInitializeTest[33804:2175226] The property is: description LoadAndInitializeTest[33804:2175226] The property is: debugDescription LoadAndInitializeTest[33804:2175226] -------Gorgeous style----- LoadAndInitializeTest[33804:2175226] The protocol is: NSObject LoadAndInitializeTest[33804:2175226] ------Gorgeous style-------- -LoadAndInitializeTest[33804:2175226] myObject.name=Si Xiaogang,myObject.age=18 LoadAndInitializeTest[33804:2175226] +[MyObjectChild load] The first two and the last + load method, we can all understand, we can ignore it directly Just fine.2175226] The property is: age LoadAndInitializeTest[33804:2175226] The property is: hash LoadAndInitializeTest[33804:2175226] The property is: superclass LoadAndInitializeTest[33804:2175226] The property is: description LoadAndInitializeTest[33804:2175226Test LoadAndInitialize[33804:2175226] The property is: debugizeDescription 2175226] -5.-------Gorgeous style----- LoadAndInitializeTest[33804:2175226] The protocol is: NSObject LoadAndInitializeTest[33804:2175226] ------Gorgeous style--- ------ LoadAndInitializeTest[33804:2175226] myObject.name=Si Xiaogang,myObject.age=18 LoadAndInitializeTest[33804:2175226] +[MyObjectChild load] The first two and the last + load method, we can all see Understand, just ignore it.2175226] The property is: age LoadAndInitializeTest[33804:2175226] The property is: hash LoadAndInitializeTest[33804:2175226] The property is: superclass LoadAndInitializeTest[33804:2175226] The property is: description LoadAndInitializeTest[33804:2175226Test] The property is: debugizeDescription 2175226] -5.-------Gorgeous style----- LoadAndInitializeTest[33804:2175226] The protocol is: NSObject LoadAndInitializeTest[33804:2175226] ------Gorgeous style--- ------ LoadAndInitializeTest[33804:2175226] myObject.name=Si Xiaogang,myObject.age=18 LoadAndInitializeTest[33804:2175226] +[MyObjectChild load] The first two and the last + load method, we can all see Understand, just ignore it.2175226] The protocol is: NSObject LoadAndInitializeTest[33804:2175226] ------Under the gorgeous style--------- LoadAndInitializeTest[33804:2175226] myObject.name= ,myObject.age=18 LoadAndInitializeTest[33804:2175226] +[MyObjectChild load] The first two and the last +load method, we can all understand it, just ignore it.2175226] The protocol is: NSObject LoadAndInitializeTest[33804:2175226] ------Under the gorgeous style--------- LoadAndInitializeTest[33804:2175226] myObject.name= ,myObject.age=18 LoadAndInitializeTest[33804:2175226] +[MyObjectChild load] The first two and the last +load method, we can all understand it, just ignore it.

The first gorgeous dividing line prints the class method. We successfully printed classFunction and load.

The second gorgeous dividing line prints the instance method. The objectFunction is first printed. What is ".cxx_destruct", research it yourself. The set and get methods of the attribute name and age are printed.

The third gorgeous dividing line prints variables. nIval is directly defined by ourselves. _name and _age are underlined variables that are automatically generated for us by attributes name and age.

The fourth gorgeous dividing line prints the attributes name and age. There is no need to explain the superclass, description, and debugDescription. Just study it yourself.

The fifth gorgeous dividing line prints the protocol NSObject protocol which I added in the header file.

At this point, the most important point begins. I instantiated a MyObject object and assigned a value to it, and then successfully printed it out. This shows that our class can be used normally. Doesn't this mean the class? Is the +load method called after the class is loaded? If it is loading, can we get so much information about the class and use the class?

Obviously, I think I am right. If there is any mistake, please correct me

Let's go back to the Apple document and start the next question

Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.

Apple said that when a class or category is added to the runtime, it is called. I used to think that when a class is added to the runtime, its +load method is called immediately, and then its sibling or subclass is loaded. That is to say, in the +load method, to get the information of its subclass or instantiate the subclass, it will not succeed, because the class has not been added to the runtime.

The subclass has been mentioned above, it is loaded after the +load of the parent class

Modify MyObjectChild.h as follows

@interface MyObjectChild: MyObject @property (nonatomic, copy) NSString *nameChild; @property (nonatomic, assign) NSInteger ageChild; @end modify MyObjectChild.m as follows

@implementation MyObjectChild{ int nIvalChild;}

  • (void)load {NSLog(@"%s", FUNCTION );}

-(void)objectFunctionChild {NSLog(@"%s", FUNCTION );}

  • (void)classFunctionChild {NSLog(@"%s", FUNCTION );} @end Add the following code at the end of [MyObject load]

    NSLog(@"------The following is a gorgeous dividing line for subclasses------");

    NSLog(@"-1.------Child------" in gorgeous style); MyObjectChild *myObjectChild = [[MyObjectChild alloc] init]; myObjectChild.age = 18; myObjectChild.name = @ " "; NSLog(@"myObjectChild.name=%@,myObjectChild.age=%ld",myObjectChild.name, myObjectChild.age);

    count = 0; metaClass = object_getClass([MyObjectChild class]); methods = class_copyMethodList(metaClass, &count); for (int i = 0; i <count; i++) {NSLog(@"Child class method is: %s", sel_getName(method_getName(methods[i])));} free(methods);

    NSLog(@"-2.------Child------" in gorgeous style); countMethod = 0; methods = class_copyMethodList([myObjectChild class], &countMethod); for (int i = 0; i <countMethod; i++) {NSLog(@"Child instance method is: %s", sel_getName(method_getName(methods[i])));} free(methods);

    NSLog(@"-3.------Child-------" in gorgeous style); countIval = 0; ivals = class_copyIvarList([myObjectChild class], &countIval); for (int i = 0 ; i <countIval; i++) {NSLog(@"Child variable is: %s", ivar_getName(ivals[i]));} free(ivals);

    NSLog(@"-4.------Child------" in gorgeous style); countProperty = 0; propertys = class_copyPropertyList([myObjectChild class], &countProperty); for (int i = 0; i <countProperty; i++) {NSLog(@"Child property is: %s", property_getName(propertys[i]));} free(propertys);

    NSLog(@"-5.------Child-------" in gorgeous style); countProtocol = 0; protocols = class_copyProtocolList([myObjectChild class], &countProtocol); for (int i = 0 ; i <countProtocol; i++) {NSLog(@"Child agreement is: %s", protocol_getName(protocols[i]));} NSLog(@"------Child----- in gorgeous style -"); Output print

LoadAndInitializeTest[55040:2254257] ------The following is the gorgeous dividing line for subclasses------ LoadAndInitializeTest[55040:2254257] -1.------Child under gorgeous style---- - LoadAndInitializeTest[55040:2254257] myObjectChild.name=Si Xiaogang,myObjectChild.age=18 LoadAndInitializeTest[55040:2254257] The Child class method is: classFunctionChild LoadAndInitializeTest[55040:2254257] The Child class method is: load LoadAndInitializeTest[55040:2254257 ] -2.------Child in a gorgeous style------- LoadAndInitializeTest[55040:2254257] Child instance method is: objectFunctionChild LoadAndInitializeTest[55040:2254257] Child instance method is: nameChild LoadAndInitializeTest[55040: 2254257] Child instance method is: setNameChild: LoadAndInitializeTest[55040:2254257] Child instance method is: ageChild LoadAndInitializeTest[55040:2254257] Child instance method is: setAgeChild: LoadAndInitializeTest[55040:2254257] Child instance method is: .cxx_destruct LoadAndInitializeTest[55040 :2254257] -3.------Gorgeous style Child------- LoadAndInitializeTest[55040:2254257] The Child variable is: nIvalChild LoadAndInitializeTest[55040:2254257] The Child variable is: _nameChild LoadAndInitializeTest[55040:2254257] The Child variable is: _ageChild LoadAndInitializeTest[55040:2254257] -4.------Child in a gorgeous style ----- LoadAndInitializeTest[55040:2254257] Child property is: nameChild LoadAndInitializeTest[55040:2254257] Child property is: ageChild LoadAndInitializeTest[55040:2254257] Child property is: hash LoadAndInitializeTest[55040:2254257] Child property is: superclass LoadAndInitializeTest [55040:2254257] The Child attribute is: description LoadAndInitializeTest[55040:2254257] The Child attribute is: debugDescription LoadAndInitializeTest[55040:2254257] -5.------Child in a gorgeous style ------- LoadAndInitializeTest[ 55040:2254257] The Child protocol is: NSObject These outputs, I will not explain, the output proves that the information of the subclass can be obtained in the +load of the parent class and the subclass can be instantiated.2254257] The Child variable is: _ageChild LoadAndInitializeTest[55040:2254257] -4.------Child in a gorgeous style------ LoadAndInitializeTest[55040:2254257] The Child property is: nameChild LoadAndInitializeTest[55040:2254257] The Child attribute is: ageChild LoadAndInitializeTest[55040:2254257] The Child attribute is: hash LoadAndInitializeTest[55040:2254257] The Child attribute is: superclass LoadAndInitializeTest[55040:2254257] The Child attribute is: description LoadAndInitializeTest[55040:2254257] The Child attribute is: debugDescriptionTest LoadAndInitial [55040:2254257] -5.------Child in a gorgeous style------- LoadAndInitializeTest[55040:2254257] Child protocol is: NSObject These outputs, I won't explain them, the output proves, In the +load of the parent class, the information of the subclass can be obtained and the subclass can be instantiated.2254257] The Child variable is: _ageChild LoadAndInitializeTest[55040:2254257] -4.------Child in a gorgeous style------ LoadAndInitializeTest[55040:2254257] The Child property is: nameChild LoadAndInitializeTest[55040:2254257] The Child attribute is: ageChild LoadAndInitializeTest[55040:2254257] The Child attribute is: hash LoadAndInitializeTest[55040:2254257] The Child attribute is: superclass LoadAndInitializeTest[55040:2254257] The Child attribute is: description LoadAndInitializeTest[55040:2254257] The Child attribute is: debugDescriptionTest LoadAndInitial [55040:2254257] -5.------Child in a gorgeous style------- LoadAndInitializeTest[55040:2254257] Child protocol is: NSObject These outputs, I won't explain them, the output proves, In the +load of the parent class, the information of the subclass can be obtained and the subclass can be instantiated.2254257] The Child attribute is: superclass LoadAndInitializeTest[55040:2254257] The Child attribute is: description LoadAndInitializeTest[55040:2254257] The Child attribute is: debugDescription LoadAndInitializeTest[55040:2254257] -5.------Child in a gorgeous style ------ LoadAndInitializeTest[55040:2254257] Child protocol is: NSObject These outputs, I will not explain, the output proves that the information of the subclass can be obtained in the +load of the parent class and the child can be instantiated Class now.2254257] The Child attribute is: superclass LoadAndInitializeTest[55040:2254257] The Child attribute is: description LoadAndInitializeTest[55040:2254257] The Child attribute is: debugDescription LoadAndInitializeTest[55040:2254257] -5.------Child in a gorgeous style ------ LoadAndInitializeTest[55040:2254257] Child protocol is: NSObject These outputs, I will not explain, the output proves that the information of the subclass can be obtained in the +load of the parent class and the child can be instantiated Class now.

What I prove is that after adding all the classes to the runtime, and then starting to call the +load method, instead of a class that Apple said, this is too challenging, please correct me.

4. The +load method is loaded according to the order of Compile Sources, but it must be called before calling [ChildClass load], and its [SuperClass load] method must be called first. Create two MyObjectSuper subclasses MyObject1 and MyObject2 in the object folder, create a MyObject subclass MyObjectChild1 in the child folder and implement the following code in three .m, then run

  • (void)load {NSLog(@"%s", FUNCTION );} output print

LoadAndInitializeTest[75083:2339602] +[MyObjectSuper load] LoadAndInitializeTest[75083:2339602] +[MyObject load] LoadAndInitializeTest[75083:2339602] +[MyObject1 load] LoadAndInitializeTest[75083:2339602] +[MyObject2 load] LoadAndInitializeTest[75083:2339602] +[MyObjectChild load] LoadAndInitializeTest[75083:2339602] +[MyObjectChild1 load] Let's now check the following Compile Sources, and the screenshot is as follows

Screenshot 2018-11-24 11.18.26.png We found that the order in Compile Sources is surprisingly consistent with the order we printed. Is this really the case? We randomly drag the arrangement order of Compile Sources, and then run

Screenshot 2018-11-24 11.28.24.png

Printout

2353132] +[MyObjectChild1 load] The second class in Compile Sources is MyObjectChild, and its parent class [MyObject load] will be called before calling [MyObjectChild load], because the +load method of the parent class has already been called, so there is no need transfer. Before calling [MyObject load], the parent class [MyObjectSuper load] will be called, because the +load method of the parent class has already been called, so there is no need to call it again. Therefore, the second class in Compile Sources prints out the fourth +load method, LoadAndInitializeTest[78440:2353132] +[MyObjectChild load] [MyObject2 load] and [MyObject1 load] are completely consistent with the above principles, please make your own reasoning. In fact, Apple s document is written like this

A class's +load method is called after all of its superclasses' +load methods. A class's +load method is called after all of its superclasses' +load methods. The conclusion of our experiment is consistent with Apple's document. If the Apple document is added, the +load method of the class is called in the order in Compile Sources, and the two rules are combined together to be perfect.

5. Call the [Category load] method after the +load method of all classes is called. The call sequence of [Category load] is completely in accordance with the order of Compile Sources. We now create a series of categories, as follows, and implement their +load methods respectively, and then run @interface MyObjectSuper (superCategory0) @interface MyObjectSuper (superCategory1)

@interface MyObject (category0) @interface MyObject (category1) @interface MyObject1 (category0) @interface MyObject1 (category1) @interface MyObject2 (category0) @interface MyObject2 (category1)

@interface MyObjectChild (category0) @interface MyObjectChild (category1) @interface MyObjectChild1 (category0) @interface MyObjectChild1 (category1)

Printout

2399103] +[MyObject1(category0) load] LoadAndInitializeTest[90277:2399103] +[MyObjectChild(category0) load] LoadAndInitializeTest[90277:2399103] +[MyObject1(category1) load] LoadAndInitializeTest[90277:2399103] +[MyObject1(category1) load] LoadAndInitializeTest[90277:2399103] +[MyObjectSuper(superCategory1) load] We found that the [category load] method will not be called until all the +load methods of the class are called. We now go to modify the order of category files in Compile Sources, we will easily find that the order in Compile Sources is always exactly the same as the order of our output. 2399103] +[MyObjectSuper(superCategory1) load] We found that the [category load] method will not be called until all the +load methods of the class are called. We now go to modify the order of category files in Compile Sources, we will easily find that the order in Compile Sources is always exactly the same as the order of our output. 2399103] +[MyObjectSuper(superCategory1) load] We found that the [category load] method will not be called until all the +load methods of the class are called. We now go to modify the order of category files in Compile Sources, we will easily find that the order in Compile Sources is always exactly the same as the order of our output.

This is what Apple s documentation says

A category +load method is called after the class s own +load method. A category s +load method is called after the class s own +load method. I can t say that it s wrong, but the conclusion I got is, [category load] is called after the +load of all classes, not after the specific class (the class). If there are any errors, please correct me.

Let's do another interesting test. We modify the main.m file as follows, and then run

int main(int argc, char * argv[]) {@autoreleasepool {NSLog(@"main"); return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));}} Print out

... LoadAndInitializeTest[95236:2418416] +[MyObjectChild(category0) load] LoadAndInitializeTest[95236:2418416] +[MyObject1(category1) load] LoadAndInitializeTest[95236:2418416] +[MyObjectChild1(category1) load] LoadAndInitializeTest[95236:2418416] ] +[MyObjectSuper(superCategory0) load] LoadAndInitializeTest[95236:2418416] +[MyObjectSuper(superCategory1) load] LoadAndInitializeTest[95236:2418416] main main turned out to be the last output, indicating that all load methods are preceded by the main function Called.

Now I summarize the use of +load:

1. The +load method is automatically called by the system after all classes are added to the runtime and before the main function is executed. 2. The system automatically calls the +load method (if any) for each class, no need to manually call, and no need to manually call [super load]. 3. The +load method will be loaded in the order of the Compile Sources where the file is located. Before calling the +load of the class, the +load method of its parent class will be called first. 4. Call the [Category load] method after the +load method of all classes is called, and the loading sequence is in accordance with the order of Compile Sources. +initialize: We have a lot of code similar to the following in the program, we call it lazy loading, first of all

@property (nonatomic, strong) UILabel *myLabel; then

  • (UILabel *)myLabel {if (!_myLabel) {_myLabel = [[UILabel alloc] init]; ...} return _myLabel;} Finally

[self addSubview:self.myLabel]; This lazy loading method is that the myLabel object will not be created until the first time a message is sent to myLabel. The +initialize method is similar in principle and is called when the class receives a message for the first time.

In fact, Apple's documentation is written in this way, that is, it is always called before the user calls.

Initializes the class before it receives its first message. Called before this class receives its first message.

Regarding the +initialize method, I summarize as follows

1. + initialize is automatically called by the system before the class receives the message for the first time, without manual calling. 2. Before calling the + initialize method of the subclass, the + initialize method of the parent class (if any) will be called first, so there is no need to manually call the [super initialize] method. 3. If there is a + initialize method in the parent class but there is no + initialize method in the child class, the child class will automatically inherit the + initialize method of the parent class, which means that the + initialize method of the parent class will be called twice. 4. The + initialize method in the Category will override the + initialize in the class. When multiple categories in the same class have implemented the +initialize method, the +initialize method of the last Category in the Compile Sources list will override the other + initialize methods. 1. + initialize is automatically called by the system before the class receives the message for the first time, without manual calling. Add the following code in MyObjectChild, and then run

  • (instancetype)init {NSLog(@"init"); if (self = [super init]) {} return self;}
  • (void)initialize {NSLog(@"%s", FUNCTION );} Then there is no + initialize output

We now add the following code in the Controller, and then run

#import "MyObjectChild.h"

@implementation ViewController

  • (void)viewDidLoad {[super viewDidLoad];

    NSLog(@"[[MyObjectChild alloc] init]; before"); [[MyObjectChild alloc] init]; NSLog(@"[[MyObjectChild alloc] init]; after");} Print output

LoadAndInitializeTest[98430:2830873] [[MyObjectChild alloc] init]; before LoadAndInitializeTest[98430:2830873] +[MyObjectChild initialize] LoadAndInitializeTest[98430:2830873] init LoadAndInitializeTest[98430:2830873] [initialize]; after The print information is output, which means that + initialize is automatically called by the system before the class receives the message for the first time, without manual calling.

2. Before calling the + initialize method of the subclass, the + initialize method of the parent class (if any) will be called first, so there is no need to manually call the [super initialize] method. Add the following code to MyObject and MyObjectSuper respectively, and then run

  • (void)initialize {NSLog(@"%s", FUNCTION );} print out

LoadAndInitializeTest[30644:2557942] +[MyObjectSuper initialize] LoadAndInitializeTest[30644:2557942] +[MyObject initialize] LoadAndInitializeTest[30644:2557942] +[MyObjectChild initialize] are printed in order from the parent class to the child class, indicating + initialize and +load The method is the same, when calling the method of the subclass, the method of the parent class will be called first. Now we add the following code in MyObjectChild, and then run

[super initialize]; printout

LoadAndInitializeTest[33679:2569542] +[MyObjectSuper initialize] LoadAndInitializeTest[33679:2569542] +[MyObject initialize] LoadAndInitializeTest[33679:2569542] +[MyObject initialize] LoadAndInitializeTest[33679:2569542] +[MyObjectChild initialize] This means that there is no need for the subclass Manually call the [super initialize] method.

3. If the parent class implements the + initialize method, but the child class does not implement + initialize, the child class will automatically inherit the + initialize of the parent class, that is, the + initialize method of the parent class will be automatically called twice. Now we Comment out the +initialize of MyObjectChild and MyObject, and then run the printout

LoadAndInitializeTest[35163:2575853] +[MyObjectSuper initialize] LoadAndInitializeTest[35163:2575853] +[MyObjectSuper initialize] LoadAndInitializeTest[35163:2575853] +[MyObjectSuper initialize] Oh my god, [MyObjectSuper initialize] was printed three times because of MyObject Will inherit the + initialize method of the parent class, and MyObjectChild will also inherit the + initialize method of the parent class, so they all inherit the + initialize method of MyObjectSuper, so they are printed three times.

I want to make a special point here. From the name of + initialize, it is an initialization function. We would think that it will be called only once, and many other blogs have clearly stated that + initialize is only called once, but in fact, it does automatically call more 2. if I have something wrong with this, I hope I can correct it.

Because the +initialize method will be automatically inherited, the error rate of +initialize is greater than that of +load.

How to write in the +initialize method, I know there are usually two ways, the first is

  • (void)initialize{ if (self == [MyClass class]) {....}} The second

  • (void)initialize {static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ ... });} 4. the + initialize method in the Category will override the + initialize in the class, and multiple categories in the same class have implemented the +initialize method At the time, the +initialize method of the last Category in the Compile Sources list will override the other +initialize methods. Add the following code in @implementation MyObjectChild (category0), and then run

  • (void)initialize {NSLog(@"%s", FUNCTION );} Print output

LoadAndInitializeTest[64871:2690309] +[MyObjectSuper initialize] LoadAndInitializeTest[64871:2690309] +[MyObject initialize] LoadAndInitializeTest[64871:2690309] +[MyObjectChild(category0) initialize] + initialize in category0 overrides + initialize in the class.

Add the following code in @implementation MyObjectChild (category1), and then run

  • (void)initialize {NSLog(@"%s", FUNCTION );} Print output

LoadAndInitializeTest[66414:2697021] +[MyObjectSuper initialize] LoadAndInitializeTest[66414:2697021] +[MyObject initialize] LoadAndInitializeTest[66414:2697021] +[MyObjectChild(category1) initialize] + initialize in category1 overwrites + initialize in category0.

Let's check it in Compile Sources. At this time, MyObjectChild category1 must be ranked behind category0. We can also change the order at will. The last category in Compile Sources must cover all other + initialize methods.

We can also modify the category method of its parent class, and find that the parent class also obeys this rule.

Author: xiaogangsi link: www.jianshu.com/p/e6979a17d... Source: Jane books are copyrighted by the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.