OC基础-category(3)

2021-03-05  本文已影响0人  我是卖报的小行家

initialize方法

initialize方法被调用的时机:
initialize方法会在 “类” 在第一次 “接收到消息” 时候被调用([MJPerson alloc],这个就是MJPerson这个类在第一次接收到消息的时候)(消息分发机制)
验证:
先创建MJPerson类,以及其分类

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface MJPerson : NSObject

@end

NS_ASSUME_NONNULL_END

#import "MJPerson.h"

@implementation MJPerson
+ (void)initialize
{
    NSLog(@"MJPerson initialize");
}
@end

分类1 MJPerson + Test1

#import "MJPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface MJPerson (Test1)

@end

NS_ASSUME_NONNULL_END

#import "MJPerson+Test1.h"

@implementation MJPerson (Test1)
+ (void)initialize
{
    NSLog(@"MJPerson (Test1) initialize");
}
@end

分类2 MJPerson + Test2

#import "MJPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface MJPerson (Test2)

@end

NS_ASSUME_NONNULL_END

#import "MJPerson+Test2.h"

@implementation MJPerson (Test2)
+ (void)initialize
{
    NSLog(@"MJPerson (Test2) initialize");
}
@end
1).如果我们从不用到MJPerson这个类,则永远不会调用initialize这个方法,这点是和load方法的一个最大区别之一
2).哪怕我们多次调用alloc ,则只输出一次,因为initialize方法会在 “类” 在第一次 “接收到消息” 时候被调用
    [MJPerson alloc];//MJPerson第一次接收消息
    [MJPerson alloc];
    [MJPerson alloc];
    [MJPerson alloc];
打印结果,且顺序是与编译顺序有关,先编译后调用

2021-03-04 17:16:45.905523+0800 Test[6043:989433] MJPerson (Test1) initialize

![打印结果](https://img.haomeiwen.com/i17524917/ab2b127a28de4fff.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

调用顺序:
如果是子类调用alloc方法,则会先调用父类的initialize方法,然后再调用自己的或者自己分类的initialize方法

    [MJStudent alloc];//MJPerson第一次接收消息
    [MJStudent alloc];
    [MJStudent alloc];
    [MJStudent alloc];

打印结果

2021-03-04 17:23:45.626093+0800 Test[6179:995502] MJPerson (Test1) initialize
2021-03-04 17:23:45.626133+0800 Test[6179:995502] MJStudent (Test2) + initialize

因为[MJPerson alloc]和[Person test]本质上就是objc_msgSend([Person class], @selector(alloc))消息发送。所以猜测应该是在objc_msgSend()方法中调用了initialize方法,并且只是在类第一次收到msg消息的时候调用一次,以后就不再调用了。而且initialize方法跟load方法不一样,不是通过指针去调用的,而是通过objc_msgSend()去调用的,这一点可以从源码可知。

void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());

Class supercls;
bool reallyInitialize = NO;

// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;

//先判断父类有没有初始化,如果没有就初始化父类
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}

// Try to atomically set CLS_INITIALIZING.
{
    monitor_locker_t lock(classInitLock);

//再判断自身是否初始化,如果没有就初始化一下
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}

    源码伪代码
    if(自己没有初始化){
        if (父类没有初始化) {
            objc_msgSend([Person class], @selector(alloc))
        }
        objc_msgSend([Student class], @selector(alloc))
    }

//如果类已经初始化过了就不会再调用initialize方法了


[MJStudent alloc];等同于以下代码

objc_msgSend([MJPerson class] @selector:(initialized))
objc_msgSend([MJStudent class] @selector:(initialized))

总结:
+initialize和+load最大区别:
+initialize是通过objc_msgSend进行调用的,所以有以下特点:
1).如果子类没有实现+initialize,则会调用父类的+initialize;
2).如果分类实现+initialize,则会覆盖父类的+initialize;(调用顺序为先编译后调用)
2)initialize
先初始化父类
再初始化子类
(可能最终调用的是父类的initialize方法)(可能会调用父类initialize多次,但不代表父类initialize多次,(父类initialize只有一次))

1、调用方式的区别
1)load是根据函数地址直接调用
2)initialize是通过objc_msgSend调用
2、调用时刻
1)load是runtine加载类、分类的时候调用(只会调用一次)
2)initialize是类第一次接收到消息的时候调用,每一个类只会initialize依次(父类的initialize方法可能会被调用多次)
3、调用顺序的区别:
+load:
1).先调用类的+load;
a.先编译的类,优先调用+load
b.调用子类的+load之前,会调用父类的+load
2).再调用分类的+load
a.先编译的分类优先调用+load

+initialize:
1)先初始化父类,再初始化子类(可能最终调用的是父类的)
上一篇下一篇

猜你喜欢

热点阅读