Objective-c

iOS进阶回顾四「load & initialize」

2019-08-27  本文已影响0人  Coder东

load方法的本质是什么?initialize呢?两个有什么区别?


#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface SFPerson : NSObject
@end
NS_ASSUME_NONNULL_END

#import "SFPerson.h"
@implementation SFPerson
+ (void)load{
    NSLog(@"SFPerson  +load");
}
@end

#import "SFPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface SFPerson (Helper)
@end
NS_ASSUME_NONNULL_END

#import "SFPerson+Helper.h"
@implementation SFPerson (Helper)
+ (void)load{
    NSLog(@"SFPerson (Helper) + load");
}
@end

#import "SFPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface SFPerson (Addtion)
@end
NS_ASSUME_NONNULL_END

#import "SFPerson+Addtion.h"

@implementation SFPerson (Addtion)

+ (void)load{
    NSLog(@"SFPerson (Addtion) + load");
}

@end

2019-08-27 14:16:08.683691+0800 load[33088:238375] SFPerson +load
2019-08-27 14:16:08.684128+0800 load[33088:238375] SFPerson (Helper) + load
2019-08-27 14:38:15.283433+0800 load[33282:256018] SFPerson (Addtion) + load

直接运行后出现上面的打印输出,我们没有导入头文件,也没有进行创建对象调用,系统直接调用load方法由此可见:
分类中也存在load方法,load方法是在程序启动时,加载类、分类的时候就会调用。在调用分类的load方法前会优先调用本类的load方法。分类中的load方法谁先编译谁先调用
我们也可以通过源码验证如下:

22E093B692B25434CAC728BFA387A090.png
可以看到类的load方法调用一次就不会调用了loadable_classes_user>0这样循环就不会再次执行了
继续查看call_class_loads()类方法
static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
    直接通过数组的下标得到我们需要的方法
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        (*load_method)(cls, SEL_load);
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

call_category_loads()分类方法的实现

static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    // Detach current loadable list.
    struct loadable_category *cats = loadable_categories;
    int used = loadable_categories_used;
    int allocated = loadable_categories_allocated;
    loadable_categories = nil;
    loadable_categories_allocated = 0;
    loadable_categories_used = 0;

    // Call all +loads for the detached list.
    for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;

        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s(%s) load]\n", 
                             cls->nameForLogging(), 
                             _category_getName(cat));
            }
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }

    // Compact detached list (order-preserving)
    shift = 0;
    for (i = 0; i < used; i++) {
        if (cats[i].cat) {
            cats[i-shift] = cats[i];
        } else {
            shift++;
        }
    }
    used -= shift;

    // Copy any new +load candidates from the new list to the detached list.
    new_categories_added = (loadable_categories_used > 0);
    for (i = 0; i < loadable_categories_used; i++) {
        if (used == allocated) {
            allocated = allocated*2 + 16;
            cats = (struct loadable_category *)
                realloc(cats, allocated *
                                  sizeof(struct loadable_category));
        }
        cats[used++] = loadable_categories[i];
    }

    // Destroy the new list.
    if (loadable_categories) free(loadable_categories);

    // Reattach the (now augmented) detached list. 
    // But if there's nothing left to load, destroy the list.
    if (used) {
        loadable_categories = cats;
        loadable_categories_used = used;
        loadable_categories_allocated = allocated;
    } else {
        if (cats) free(cats);
        loadable_categories = nil;
        loadable_categories_used = 0;
        loadable_categories_allocated = 0;
    }

    if (PrintLoading) {
        if (loadable_categories_used != 0) {
            _objc_inform("LOAD: %d categories still waiting for +load\n",
                         loadable_categories_used);
        }
    }

    return new_categories_added;
}
#import "SFPerson.h"

@implementation SFPerson

+ (void)load{
    NSLog(@"SFPerson  +load");
}
+(void)run{
    NSLog(@"SFPerson  +run");

}
@end

利用[SFStudent run];进行调用输入如下:

2019-08-27 14:45:34.859386+0800 load[33449:263656] SFPerson +load
2019-08-27 14:45:34.859677+0800 load[33449:263656] SFPerson (Helper) + load
2019-08-27 14:45:34.859693+0800 load[33449:263656] SFPerson (Addtion) + load
2019-08-27 14:45:34.859769+0800 load[33449:263656] SFPerson (Addtion) +run

由此可知:分类中重写类方法时,分类的类方法会优先调用。我们利用SFStudent调用run方法时,输出为SFPerson (Addtion) +run优先调用的原因

下面继续initialize分析:

#import "SFPerson.h"

@implementation SFPerson

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

main.m中调用如下

#import <Foundation/Foundation.h>
#import "SFStudent.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [SFStudent run];
    }
    return 0;
}

2019-08-27 15:39:29.802479+0800 load[39941:335527] SFPerson initialize
2019-08-27 15:39:29.802491+0800 load[39941:335527] SFPerson initialize

#import <Foundation/Foundation.h>
#import "SFStudent.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        [SFStudent run];
        [SFStudent run];
        [SFStudent run];

    }
    return 0;
}

2019-08-27 16:20:53.860266+0800 load[40134:360148] SFPerson initialize
2019-08-27 16:20:53.860282+0800 load[40134:360148] SFPerson initialize

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

调用时间:

  • load方法是可以继承的,但是一般情况下,都是不会主动调用load方法,都是让系统自动调用
  • + initialize方法会在类第一次接收消息的时候调用
    调用顺序:
  • 先调用父类的+initialize,在调用子类+initialize父类的初始化方法可能调用多次, 先初始化父类再初始化子类,而且每个类只会初始化一次, 如果分类实现了+initialize,就覆盖类本身的+initialize方法
  • 先调用类的load方法再去调用分类的load,先编译的类,先调用,调用子类的load之前,先调用父类的load
    调用方式的:
  • +initialize是通过objc_msgSend进行调用的
  • load根据函数地址直接调用,在runtime加载类分类的时候调用,只会调用一次
上一篇下一篇

猜你喜欢

热点阅读