美文网首页2021面试
iOS+load和+initialize详解

iOS+load和+initialize详解

作者: MaybeLove00 | 来源:发表于2018-05-17 15:49 被阅读0次

    转自:
    iOS类方法load和initialize详解
    iOS - + initialize 与 +load

    load函数调用特点如下:

    +(void)load
    什么时候调用,当程序一启动的时候就会调用
    作用:将当前类加载进内存,放在代码区

    +load 方法会系统被默认执行,并且是在main函数之前执行的,与这个类是否被用到无关,

    1.当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类
    2.当子类未实现load方法时,不会调用父类load方法
    3.类中的load方法执行顺序要优先于类别(Category)
    4.当有多个类别(Category)都实现了load方法,这几个load方法都会执行,
    但执行顺序不确定(其执行顺序与类别在Compile Sources中出现的顺序一致)
    5.当然当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致
    
    下面通过实例来一起验证下:
    我们新建2个类:Person继承NSObject,Student和Teacher均继承Person
    
    Person : NSObject
    Student : Person
    Teacher : Person
    
    新建3个Person分类:
    
    Person (Category)
    Person (Category2)
    Person (Category3)
    
    在Person,Student,Person (Category),Person (Category2),Person (Category3)的.m文件中实现下面方法(Teacher除外)
    
    //In Person.m
    +(void)load
    {
        NSLog(@"%s",__FUNCTION__);
    }
    //In Student.m(继承自Person)
    +(void)load
    {
        NSLog(@"%s",__FUNCTION__);
    }
    
    //In Teacher.m(继承Person)
    不实现load方法
    
    //In Person+Category.m
    +(void)load
    {
        NSLog(@"%s",__FUNCTION__);
    }
    //In Person+Category2.m
    +(void)load
    {
        NSLog(@"%s",__FUNCTION__);
    }
    //In Person+Category3.m
    +(void)load
    {
        NSLog(@"%s",__FUNCTION__);
    }
    运行结果:
    +[Person load]
    +[Student load]
    +[NSObject(Category3) load]
    +[NSObject(Category1) load]
    +[NSObject(Category2) load]
    
    可以看到执行顺序依次为:
    
    1.首先执行的是父类Person load方法,再执行的是子类Student load方法,说明父类的load方法执行顺序要优先于子类
    2.子类Teacher中没有实现load方法,没有打印,说明子类没有实现load方法时并不会调用父类load方法
    3.最后执行的是Person 3个Category的load方法,并且没有顺序,说明类别(Category)中的load方法要晚于类,
    多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定
    4.同时我们也可以看到这几个Category load 执行顺序与其在Compile Sources中出现的顺序一致
    5.当然多个不同的类 其load执行顺序,也与其在Compile Sources出现的顺序一致
    
    

    initialize函数调用特点如下:

    initialize在类或者其子类的第一次初始化这个类之前调用。即使类文件被引用进项目,但是没有使用,initialize不会被调用。

    • initialize 方法类似一个懒加载,如果没有使用这个类,那么系统默认不会去调用这个方法,且默认只加载一次;
    • initialize 的调用发生在 +init 方法之前。
    1.父类的initialize方法会比子类先执行
    2.当子类未实现initialize方法时,会调用父类initialize方法,
    子类实现initialize方法时,会覆盖父类initialize方法.
    3.当有多个Category都实现了initialize方法,会覆盖类中的方法,
    只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)
    

    我们同样通过实例来一起验证下:

    说明: 只是把类文件被引用进项目,没有使用的话,initialize不会被调用

    我们添加Person类,在.m中实现+ initialize方法

    //In Person.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    运行结果:
    无打印...
    啥也没打印
    说明:只是把类文件被引用进项目,没有使用的话,initialize不会被调用
    我们在Teacher(继承Person).m中不实现initialize方法
    
    //In Person.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    //In Teacher.m(继承自Person)
    
    Teacher.m中不实现initialize方法
    
    我们调用下Teacher的new方法,运行
    
    [Teacher new];
    
    运行结果:
    +[Person initialize]
    +[Person initialize]
    运行后发现父类的initialize方法竟然调用了两次:
    可见当子类未实现initialize方法,会调用父类initialize方法.
    但为什么会打印2次呢?
    我的理解:
    子类不实现initialize方法,会把继承父类的initialize方法并调用一遍。
    在此之前,父类初始化时,会先调用一遍自己initialize方法.所以出现2遍,
    所以为了防止父类initialize中代码多次执行,我们应该这样写:
    //In Person.m
    +(void)initialize
    {
        if(self == [Person class])
        {
              NSLog(@"%s",__FUNCTION__);
        }
    }
    
    下面我们在 Teacher.m中实现initialize方法
    //In Person.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    //In Teacher.m(继承自Person)
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    同样调用Teacher 的new方法,运行
    
    [Teacher new];
    
    运行结果:
    +[Person initialize]
    +[Teacher initialize]
    可以看到 当子类实现initialize方法后,会覆盖父类initialize方法,这一点和继承思想一样
    
    我们在Person.m和Person+Category.m中实现initialize方法
    //In Person.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    //In Person+Category.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    调用Person 的 new方法,运行
    [Person new];
    
    运行结果:
    2017-05-05 09:46:51.054 LoadAndInitializeExample[38773:1226306] +[Person(Category) initialize]
    运行后可以看到Person的initialize方法并没有被执行,已经被Person+Category中的initialize取代了
    当有多个Category时会怎样了,我们在Person,Person+Category,Person+Category2,Person+Category3 的.m中都实现initialize方法
    //In Person.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    //In Person+Category.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    //In Person+Category2.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    //In Person+Category3.m
    +(void)initialize
    {
         NSLog(@"%s",__FUNCTION__);
    }
    
    调用Person new方法,运行
    [Person new];
    运行结果:
    2017-05-05 09:49:38.853 LoadAndInitializeExample[38825:1227819] +[Person(Category2) initialize]
    可以看到,当存在多个Category时,也只执行一个,具体执行哪一个Category中的initialize方法,测试后便可发现,会执行Compile Sources 列表中最后一个Category 的initialize方法
    
    

    什么情况下使用:

    由于调用load方法时的环境很不安全,我们应该尽量减少load方法的逻辑。另一个原因是load方法是线程安全的,它内部使用了锁,所以我们应该避免线程阻塞在load方法中

    load很常见的一个使用场景,交换两个方法的实现

    //摘自MJRefresh
    + (void)load
    {
        [self exchangeInstanceMethod1:@selector(reloadData) method2:@selector(mj_reloadData)];
        [self exchangeInstanceMethod1:@selector(reloadRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_reloadRowsAtIndexPaths:withRowAnimation:)];
        [self exchangeInstanceMethod1:@selector(deleteRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_deleteRowsAtIndexPaths:withRowAnimation:)];
        [self exchangeInstanceMethod1:@selector(insertRowsAtIndexPaths:withRowAnimation:) method2:@selector(mj_insertRowsAtIndexPaths:withRowAnimation:)];
        [self exchangeInstanceMethod1:@selector(reloadSections:withRowAnimation:) method2:@selector(mj_reloadSections:withRowAnimation:)];
        [self exchangeInstanceMethod1:@selector(deleteSections:withRowAnimation:) method2:@selector(mj_deleteSections:withRowAnimation:)];
        [self exchangeInstanceMethod1:@selector(insertSections:withRowAnimation:) method2:@selector(mj_insertSections:withRowAnimation:)];
    }
    
    + (void)exchangeInstanceMethod1:(SEL)method1 method2:(SEL)method2
    {
        method_exchangeImplementations(class_getInstanceMethod(self, method1), class_getInstanceMethod(self, method2));
    }
    

    initialize方法主要用来对一些不方便在编译期初始化的对象进行赋值。比如NSMutableArray这种类型的实例化依赖于runtime的消息发送,所以显然无法在编译器初始化:

    // In Person.m
    // int类型可以在编译期赋值
    static int someNumber = 0; 
    static NSMutableArray *someArray;
    + (void)initialize {
        if (self == [Person class]) {
            // 不方便编译期复制的对象在这里赋值
            someArray = [[NSMutableArray alloc] init];
        }
    }
    
    

    注意: 在initialize方法收到调用时,运行环境基本健全。 initialize内部也使用了锁,所以是线程安全的。但同时要避免阻塞线程,不要再使用锁

    总结:
    load和initialize的共同点
    1.如果父类和子类都被调用,父类的调用一定在子类之前
    2.自动调用父类,不需要super操作; load应该是在load方法内部实现, 系统会帮我们调用super load。不用我们在显示调用 , 自动调用仅仅会调用一次(不包括外部显示调用)。

    load和initialize的区别.png

    相关文章

      网友评论

        本文标题:iOS+load和+initialize详解

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