The Load and initialize

+ (void)load

  1. Each class and category in the system is run as soon as it is loaded into the Runtime and is called only once. If the load method is called for both the class and the class to which it belongs, the load method is called first for the class and then for the class.

  2. The load method does not follow inheritance rules. That is, if a class does not have a load method, the system will not call it, regardless of whether its superclass implements a load method

+ (void)initialize

  1. For each class in the system, this method is called only once, before the class is first used by the program. It is called by the run-time system and should never be called directly from code.

  2. As with other messages, if a class does not implement it and its superclass does, the superclass implementation code is run.

Note:

Load comes before main.m ~ is loaded according to the order in Compile Sources

+initialize() of the parent class may be called multiple times

Initialize the

There are two types of initializers (initialization methods) for classes (structs, enumerations) :

  • Designated Initializers

  • Convenience Initializers

Designated Initializers

The specified initializer is the primary initializer of a class (struct, enumeration). When a class (struct, enumeration) is initialized, it must call its own or its parent’s specified initializer. A class (struct, enumeration) can have multiple designated initializers that represent initializations from different sources. It is not recommended to create multiple specified initializers for a class (struct, enumeration) unless there are multiple different sources to initialize it. In iOS, view control classes such as UIView and UIViewController have two designated initializers, one for code initialization and the other for Nib initialization

Convenience Initializers

Convenience initializers are secondary initializers for classes (structs, enumerations) that make it easier to set related properties (member variables) during initialization. Since convenience initializers are for convenience, a class (struct, enumeration) can have multiple convenience initializers, each of which eventually calls its own designated initializer

Core iOS initialization rules

  1. There must be at least one designated initializer in which all non-optional type attributes are properly initialized (with values)
  2. Convenience initializers must call other initializers, such that the specified initializer is sure to be called eventually

Objective-C

Objective-c automatically assigns each attribute (member variable) a value of 0 or nil during initialization. There is no requirement to assign an additional value to each attribute (member variable), which is convenient and lacks the security of the code.

The designated initializers in Objective-C are later modified by NS_DESIGNATED_INITIALIZER. Here are the designated initializers for NSObject and UIView

// NSObject
@interface NSObject <NSObject> 

- (instancetype)init
#if NS_ENFORCE_NSOBJECT_DESIGNATED_INITIALIZER
    NS_DESIGNATED_INITIALIZER
#endif
    ;
@end

// UIView
@interface UIView : UIResponder

- (instancetype)initWithFrame:(CGRect)frame NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

@end
Copy the code

In Objective-C, all classes inherit from NSObject. When you customize a class, you either inherit directly from NSObject, or you inherit from UIView or some other class.

No matter what class it inherits from, a new initialization method is often required, and this new initialization method is actually a new designated initializer. If a new designated initializer exists, the original designated initializer is automatically degraded to a convenience initializer. To comply with the rule that you must call the designated initializer, you must override the old fixed initializer and call the new designated initializer inside, which ensures that all attributes (member variables) are initialized

According to this rule, we can see from NSObject UIView that the designated initializer -init of NSObject degrades to a convenience initializer because UIView has a new designated initializer -initWithFrame:. So when [[UIView alloc] init] is called, -init must call -initWithFrame:

When there is a new designated initializer, it is recommended to add NS_DESIGNATED_INITIALIZER after the method name to actively inform the compiler that there is a new designated initializer. This allows you to use Xcode’s built-in Analysis function to identify potential vulnerabilities during initialization

@interface MyView : UIView @property (nonatomic, strong) NSString *name; NS_DESIGNATED_INITIALIZER - (instancetype)initWithFrame:(CGRect)frame name:(NSString *)name is recommended NS_DESIGNATED_INITIALIZER; @end@implementation MyView // implement MyView This method has become the new specified initializer - (instancetype)initWithFrame:(CGRect)frame name:(NSString *)name {if (self = [super initWithFrame:frame]) { self.name = name; } return self; } // The old designated initializer is automatically degenerated into a convenience initializer, Must call the new specified initializer inside - (instanceType)initWithFrame:(CGRect)frame {return [self initWithFrame:frame name:@"Daniels"]; } // the old designated initializer is automatically degenerated into a convenience initializer, which must be used to call the new designated initializer - (instancetype)initWithCoder:(NSCoder *)coder { Return [self initWithFrame:CGRectNull name:@"Daniels"]; } @endCopy the code

If you don’t want to override the old designated initializer, but you don’t want to have bugs or vulnerabilities, you can use NS_UNAVAILABLE to make the old designated initializer obsolete, so the outside world can’t call the old designated initializer

@interface MyView : UIView @property (nonatomic, strong) NSString *name; NS_DESIGNATED_INITIALIZER - (instancetype)initWithFrame:(CGRect)frame name:(NSString *)name is recommended NS_DESIGNATED_INITIALIZER; // Discard the old specified initializer - (instanceType)init NS_UNAVAILABLE; // discard the old specified initializer - (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE; // discard the old specified initializer - (instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE; @end@implementation MyView // implement MyView This method has become the new specified initializer - (instancetype)initWithFrame:(CGRect)frame name:(NSString *)name {if (self = [super initWithFrame:frame]) { self.name = name; } return self; } @endCopy the code

Case Study

//MessagePromptView.h

@interface MessagePromptView : UIView

- (instancetype)initWithMessage:(NSString *)message NS_DESIGNATED_INITIALIZER;

@end

//MessagePromptView.m

@interface MessagePromptView ()

@property (nonatomic, copy) NSString *message;

@end

@implementation MessagePromptView

- (instancetype)initWithMessage:(NSString *)message{
    if (self = [super initWithFrame:CGRectZero]) {
        _message = message;
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame{
    return [self initWithMessage:nil];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    return [self initWithMessage:nil];
}

@end
Copy the code