美文网首页
OC基础-category(2)

OC基础-category(2)

作者: 我是卖报的小行家 | 来源:发表于2021-03-04 10:41 被阅读0次

Category的load方法

定义一个ZKPerson类,然后类扩展出来两个分类(Test)(Test1)如下图

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface ZKPerson : NSObject
+(void)test;
@end

#import "ZKPerson.h"

@implementation ZKPerson
+ (void)load
{
    NSLog(@"ZKPerson - +load");
}
+ (void)test
{
    NSLog(@"ZKPerson - +test");
}
@end

Test

#import "ZKPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface ZKPerson (Test)
+(void)test;
@end

NS_ASSUME_NONNULL_END

#import "ZKPerson+Test.h"

@implementation ZKPerson (Test)
+ (void)load
{
    NSLog(@"ZKPerson (Test)- +load");
}
+ (void)test //这个警告的意思是, 在category中重写了原类的方法
{
    NSLog(@"ZKPerson(Test) - +test");
}
@end

Test1


#import "ZKPerson.h"

NS_ASSUME_NONNULL_BEGIN

@interface ZKPerson (Test1)
+(void)test;
@end

NS_ASSUME_NONNULL_END

#import "ZKPerson+Test1.h"
@implementation ZKPerson (Test1)
+ (void)load
{
    NSLog(@"ZKPerson (Test1)- +load");
}
+ (void)test //这个警告的意思是, 在category中重写了原类的方法
{
    NSLog(@"ZKPerson(Test1) - +test");
}
@end

调用 [ZKPerson test];查看打印结果如下

2021-03-04 10:37:18.951342+0800 Test[87457:728941] ZKPerson - +load
2021-03-04 10:37:18.951808+0800 Test[87457:728941] ZKPerson (Test)- +load
2021-03-04 10:37:18.951891+0800 Test[87457:728941] ZKPerson (Test1)- +load
2021-03-04 10:37:18.952038+0800 Test[87457:728941] ZKPerson(Test1) - +test
Program ended with exit code: 0

这里只打印了Test1分类的方法,因为编译时顺序Test1分类比Test分类后编译。但是注意,每个类的+load方法都执行了一次这是为什么?我们查看runtime源码:

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
//首先会调用类的+load方法
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
//然后再调用分类的+load方法
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

由源码可知,每个类的+load方法都将调用一次。
我们点进去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);
}

我们看到+load方法的调用跟普通的方法调用不一样,它是直接从类里面拿到这个方法的地址,然后直接调用。所以每个类的+load方法都会调用。

+load方法的调用顺序

由上面源码可知,很显然+load方法的调用顺序是先调用原有类的+load方法,然后再调用分类的+load方法。那么原有类的+load方法调用顺序呢?如果有多个分类其调用顺序又是怎样呢?
先看原有类的+load方法调用顺序

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertWriting();

    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
//将类放入数组中
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
//递归,将父类传进去,把父类加进数组中
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}

从上面schedule_class_load()方法中可以看到会优先调用父类的+load方法。如果没有继承关系,那么就会按照编译的顺序去调用,先编译,先调用。
+load方法调用顺序总结:

1、首先是调用类的+load方法,按类的编译顺序调用(先编译,先调用),优先编译的先调用,调用子类的+load方法之前优先调用父类的+load方法
2、然后调用分类的+load方法,按类的编译顺序调用(先编译,先调用),优先编译的先调用

category中有load方法,且是在runtime加载类,分类的时候被调用
问题:load方法能继承吗?
答案:一般我们是不会主动去调用+load方法的,会等系统调用,一般继承是在我们调用方法的时候才会有意义。(存在于子类和父类之间)。是可以继承的,子类没有,去找父类的load方法
相当于给子类发一个消息机制,objc_msgSend([ZKStudent class] @selector(load)) isa->元类未找到,通过superclass找到父元类里面的类方法

本文参考此篇华文 https://www.jianshu.com/p/b21d3d717d27

相关文章

  • OC基础-category(2)

    Category的load方法 定义一个ZKPerson类,然后类扩展出来两个分类(Test)(Test1)如下图...

  • OC知识点整理-(零)内容概要

    我们在OC中基础中,尝尝会被问到下面几个基础方面的问题。 OC基础 Category 关联对象 代理,通知 KVO...

  • ios内存管理

    速查备忘 OC基础之类别(Category)和扩展(Extension): https://www.jianshu...

  • OC基础-(一)Category

    什么是Category(分类)? Category是Objctive-C 2.0之后所添加的语言特性,可用于为类添...

  • OC基础-category(3)

    initialize方法 initialize方法被调用的时机:initialize方法会在 “类” 在第一次 “...

  • OC基础-category(1)

    首先我们要明白什么是category?类扩展class extension我们有一个ZKPerson类如下里面会有...

  • 结合 category 工作原理分析 OC2.0 中的 runt

    结合 category 工作原理分析 OC2.0 中的 runtime 结合 category 工作原理分析 OC...

  • OC--Category、AssociatedObject

    Category原理、作用深入理解Objective-C:Category (OC1.0)结合 category ...

  • 容易被忽视的类扩展

    基础知识 类扩展(class extension)是一种特殊的OC类别(category),它没有类别名,小括号里...

  • Category与Extension

    Category概述:Category是OC2.0之后添加的语言特性,Category又叫类别,分类等,能够在不改...

网友评论

      本文标题:OC基础-category(2)

      本文链接:https://www.haomeiwen.com/subject/ceghqltx.html