NSObject的load和initialize方法(转)

  全文转载自:http://www.cocoachina.com/ios/20150104/10826.html

  在Objective-C中,NSObject是根类,而NSObject.h的头文件中前两个方法就是load和initialize两个类方法,本篇文章就对这两个方法做下说明和整理。

1、概述

  Objective-C作为一门面向对象语言,有类和对象的概念。编译后,类相关的数据结构会保留在目标文件中,在运行时得到解析和使用。

  在应用程序运行起来的时候,类的信息会有加载和初始化过程。其实在Java语言中也有类似的过程,JVM的ClassLoader也对类进行了加载、连接、初始化。

  就像Application有生命周期回调方法一样,在Objective-C的类被加载和初始化的时候,也可以收到方法回调,可以在适当的情况下做一些定制处理。而这正是load和initialize方法可以帮我们做到的。

+ (void)load;
+ (void)initialize;

   可以看到这两个方法都是以“+”开头的类方法,返回为空。通常情况下,我们在开发过程中可能不必关注这两个方法。如果有需要定制,我们可以在自定义的NSObject子类中给出这两个方法的实现,这样在类的加载和初始化过程中,自定义的方法可以得到调用。

  从如上声明上来看,也许这两个方法和其它的类方法相比没什么特别。但是,这两个方法具有一定的“特殊性”,这也是这两个方法经常会被放在一起特殊提到的原因。详细请看如下几小节的整理。

  

  

  load和initialize的共同特点

    load和initialize有很多共同特点,下面简单列一下:

      • 在不考虑开发者主动使用的情况下,系统最多会调用一次
      • 如果父类和子类都被调用,父类的调用一定在子类之前
      • 都是为了应用运行提前创建合适的运行环境
      • 在使用时都不要过重地依赖于这两个方法,除非真正必要

  

load方法相关要点 

        • 调用时机比较早,运行环境有不确定因素。具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,必要时还要自己负责做auto release处理。
        • 补充上面一点,对于有依赖关系的两个库中,被依赖的类的load会优先调用。但在一个库之内,调用顺序是不确定的。
        • 对于一个类而言,没有load方法实现就不会调用,不会考虑对NSObject的继承。
        • 一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前。
        • Category的load也会收到调用,但顺序上在主类的load调用之后。
        • 不会直接触发initialize的调用。

  

initialize方法相关要点

      • initialize的自然调用是在第一次主动使用当前类的时候(lazy,这一点和Java类的“clinit”的很像)。
      • 在initialize方法收到调用时,运行环境基本健全。
      • initialize的运行过程中是能保证线程安全的。
      • 和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,同样不需要super调用。

  由于initialize的这些特点,使得其应用比load要略微广泛一些。可用来做一些初始化工作,或者单例模式的一种实现方案。

  

原理

“源码面前没有秘密”。最后,我们来看看苹果开放出来的部分源码。从中我们也许能明白为什么load和initialize及调用会有如上的一些特点。

其中load是在objc库中一个load_images函数中调用的,先把二进制映像文件中的头信息取出,再解析和读出各个模块中的类定义信息,把实现了load方法的类和Category记录下来,最后统一执行调用。

//其中的prepare_load_methods函数实现如下:

void prepare_load_methods(header_info *hi)
{
    Module mods;
    unsigned int midx;
    if (_objcHeaderIsReplacement(hi)) {
        return;
    }

    mods = hi->mod_ptr;
    for (midx = 0; midx < hi->mod_count; midx += 1)
    {
        unsigned int index;

        if (mods[midx].symtab == nil)
            continue;

        for (index = 0; index < mods[midx].symtab->cls_def_cnt; index += 1)
        {
            Class cls = (Class)mods[midx].symtab->defs[index];
            if (cls->info & CLS_CONNECTED) {
                schedule_class_load(cls);
            }
        }
    }
    mods = hi->mod_ptr;

    midx = (unsigned int)hi->mod_count;
    while (midx-- > 0) {
        unsigned int index;
        unsigned int total;
        Symtab symtab = mods[midx].symtab;

        if (mods[midx].symtab == nil)
            continue;
        total = mods[midx].symtab->cls_def_cnt +
            mods[midx].symtab->cat_def_cnt;

        index = total;
        while (index-- > mods[midx].symtab->cls_def_cnt) {
            old_category *cat = (old_category *)symtab->defs[index];
            add_category_to_loadable_list((Category)cat);
        }
    }
}

    这大概就是主类中的load方法先于category的原因。再看下面这段:

static void schedule_class_load(Class cls)
{
    if (cls->info & CLS_LOADED) return;
    if (cls->superclass) schedule_class_load(cls->superclass);
    add_class_to_loadable_list(cls);
    cls->info |= CLS_LOADED;
}

    

这正是父类load方法优先于子类调用的原因。

再来看下initialize调用相关的源码。objc的库里有一个_class_initialize方法实现,如下:

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

    Class supercls;
    BOOL reallyInitialize = NO;

    supercls = cls->superclass;
    if (supercls  &&  !supercls->isInitialized()) {
        _class_initialize(supercls);
    }

    monitor_enter(&classInitLock);
    if (!cls->isInitialized() && !cls->isInitializing()) {
        cls->setInitializing();
        reallyInitialize = YES;
    }
    monitor_exit(&classInitLock);

    if (reallyInitialize) {
        _setThisThreadIsInitializingClass(cls);

        if (PrintInitializing) {
            _objc_inform("INITIALIZE: calling +[%s initialize]",
                         cls->nameForLogging());
        }

        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);

        if (PrintInitializing) {
            _objc_inform("INITIALIZE: finished +[%s initialize]",
                         cls->nameForLogging());
        }

        monitor_enter(&classInitLock);
        if (!supercls  ||  supercls->isInitialized()) {
            _finishInitializing(cls, supercls);
        } else {
            _finishInitializingAfter(cls, supercls);
        }
        monitor_exit(&classInitLock);
        return;
    }

    else if (cls->isInitializing()) {
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else {
            monitor_enter(&classInitLock);
            while (!cls->isInitialized()) {
                monitor_wait(&classInitLock);
            }
            monitor_exit(&classInitLock);
            return;
        }
    }

    else if (cls->isInitialized()) {
        return;
    }

    else {
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}

    在这段代码里,我们能看到initialize的调用顺序和线程安全性。

  

时间: 2024-11-05 22:40:10

NSObject的load和initialize方法(转)的相关文章

NSObject的load和initialize方法

load和initialize的共同特点 在不考虑开发者主动使用的情况下,系统最多会调用一次 如果父类和子类都被调用,父类的调用一定在子类之前 都是为了应用运行提前创建合适的运行环境 在使用时都不要过重地依赖于这两个方法,除非真正必要 load和initialize的区别 load方法 调用时机比较早,运行环境有不确定因素.具体说来,在iOS上通常就是App启动时进行加载,但当load调用的时候,并不能保证所有类都加载完成且可用,必要时还要自己负责做auto release处理.对于有依赖关系的

细说OC中的load和initialize方法

OC中有两个特殊的类方法,分别是load和initialize.本文总结一下这两个方法的区别于联系.使用场景和注意事项.Demo可以在我的Github上找到——load和initialize,如果觉得有帮助还望点个star以示支持,总结在文章末尾. load 顾名思义,load方法在这个文件被程序装载时调用.只要是在Compile Sources中出现的文件总是会被装载,这与这个类是否被用到无关,因此load方法总是在main函数之前调用. 调用规则 如果一个类实现了load方法,在调用这个方法

load和initialize方法

  一.load 方法什么时候调用: 在main方法还没执行的时候 就会 加载所有类,调用所有类的load方法. load方法是线程安全的,它使用了锁,我们应该避免线程阻塞在load方法. 在项目中使用的一些场景: 比如我们要统计所有页面(UIViewController.UITableViewController)的数据,可以在UIViewController的类别里的load方法里实现Method Swizzle @implementation UIViewController (BaseM

Load与initialize方法

为了讲清楚饿汉式单例模式实现需要了解一下这两个方法.它们的特别之处,在于iOS会在运行期提前并且自动调用这两个方法,而且很多对于类方法的规则(比如继承,类别(Category))都有不同的处理因为这两个方法是在程序运行一开始就被调用的方法,我们可以利用他们在类被使用前,做一些预处理工作.比如我碰到的就是让类自动将自身类名保存到一个NSDictionary中.Apple的文档就不再这里给出了,可以自己去看看. Apple的文档很清楚地说明了initialize和load的区别在于:load是只要类

iOS load方法与initialize方法

在 iOS 开发中,我们经常会使用 +load 方法来做一些在 main 函数之前的操作,比如方法交换(Method Swizzle)等.现在分析一下load方法跟initialize方法的调用顺序以及区别. 1.先看下load方法 尝试定义一个继承自 NSObject 的 Person 类,并对其添加两个分类 Life 和 Work:再定义一个 Student 类继承自 Person,并对其添加 School 分类.在以上所有类和分类中,均实现 +load: #import "Person.h

iOS开发-OC篇 load方法 和 initialize方法比较

Load方法 和 initialize方法的比较    在OC语言中,我们相比之下对于load和initialize方法的使用比较少,所以会不是很清楚的了解二者的用途和区别,所以整理了一下,和大家进行分享,有所得不对的地方,希望能够指出来,多谢! 1.load方法特点: 1> 当类被引用进程序的时候会执行这个函数 2> 一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前. 3> Category的load也会收到调用,但顺序上在主类的load调用之后.

iOS load和initialize的区别

可能有些还不清楚load和initialize的区别,下面简单说一下: 首先说一下 + initialize 方法:苹果官方对这个方法有这样的一段描述:这个方法会在 第一次初始化这个类之前 被调用,我们用它来初始化静态变量. initialize方法的调用时机,当向该类发送第一个消息(一般是类消息首先调用,常见的是alloc)的时候,先调用类中的,再调用类别中的(类别中如果有重写):如果该类只是引用,没有调用,则不会执行initialize方法.两者方法的共同点:自动调用父类的,不需要super

黑马程序员-OC类的本质,深入探讨,load方法和initialize方法

1:类的本质:类也是一种类,可以叫做类类,类对象,类类型: 2:类和对象在内存中分配问题(注意区分类的对象和类对象的概念) 类对象在内存中只有一份,且只加载一次,类对象中存放了类中定义的方法: 而成员变量和isa指针,存放在了类的对象中;isa指针指向了类对象:如图: 3:类本身也是对象,是class类型的对象: // 以person为例 Person *p1 = [[Person alloc] init]; Person *p1 = [[Person alloc] init]; // 获取类对

iOS Load方法 和 initialize方法的比较

一.load方法特点: 1. 当类被引用进程序的时候会执行这个函数 2.一个类的load方法不用写明[super load],父类就会收到调用,并且在子类之前. 3.Category的load也会收到调用,但顺序上在主类的load调用之后. 二.initialize方法特点: 1. initialize的自然调用是在第一次主动使用当前类的时候 2.和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍.注意的是在此之前,父类的方法已经被执行过一次了,同样不需要su