Objective-C中的+initialize和+load

写在前面

近几天花了一些时间了解了一下Objective-C runtime相关的东西,其中涉及到了+load方法,譬如method swizzling通常在category的+load方法中完成。之前对initializer和load的使用就比较疑惑,但一直没有详细去对比了解,以此为契机,集各方资源,分析一下吧!

关于了解+initialize+load,个人感觉参考官方文档《NSObject Class Reference》就够了。

+initialize

关于+initialize方法,《NSObject Class Reference》的介绍如下:

Initializes the class before it receives its first message.

可以理解+initialize的作用是为了该Class在使用前创建合适的环境;

关于其使用,《NSObject Class Reference》的说明如下:

The runtime sends initialize to each class in a program just before the class, or any class that inherits from it, is sent its first message from within the program. The runtime sends the initialize message to classes in a thread-safe manner. Superclasses receive this message before their subclasses. The superclass implementation may be called multiple times if subclasses do not implement initialize—the runtime will call the inherited implementation—or if subclasses explicitly call [super initialize].

这上面这段话,可以得出如下这么一些意思:

  • +initialize方法是在runtime被调用的;
  • 对于某个类,其类+initialize方法都会在该对象接受任何消息之前被调用;
  • 如果父类和子类的+initialize方法都被调用,父类的调用一定在子类之前,这是系统自动完成的,子类+initialize中没必要显式调用[super initialize];
  • runtime系统处理+initialize消息的方式是线程安全的,所以没必要在+initialize中为了保证线程安全而使用lock、mutex之类的线程安全工具;
  • 某个类的+initialize的方法不一定只被调用一次,至少有两种情况会被调用多次:
    • 子类显式调用[super initialize];
    • 子类没有实现+initialize方法;

下面以示例演示某个类的+initialize被多次执行的现象。

定义三个类:Person、Student、Teacher,Student和Teacher继承自Person,Person继承自NSObject。Person和Student都实现了+initialize方法,Teacher没有实现该方法,如下:

// Person的+initialize方法的实现
+ (void)initialize {
    NSLog(@"Person initialize");
}

// Student的+initialize方法的实现
+ (void)initialize {
    NSLog(@"Student initialize");
}

执行效果如下:

- (void)viewDidLoad {
    Student *aStudent = [[Student alloc] init];
    Teacher *aTeacher = [[Teacher alloc] init];

    [super viewDidLoad];
}

/* 输出:
Person initialize
Student initialize
Person initialize
*/

可以看到,对于Student,在其+initialize方法被调用之前,其super class(Person)的+initialize方法被率先调用;对于Teacher,没有定义+initialize方法,所以它会直接调用super class(Person)的+initialize方法,这就导致了Person的+initialize方法被执行两次。

有没有办法避免Person的+initialize方法被多次调用?当然可以:

// Person的+initialize方法的实现
+ (void)initialize {
    static BOOL b = false;
    if (!b) {
        NSLog(@"Person initialize");
        b = true;
     }
}

也可以这样:

// Person的+initialize方法的实现
+ (void)initialize {
    if (self == [Person class]) {
        NSLog(@"Person initialize");
    }
}

《NSObject Class Reference》中还对+initialize方法的使用做了一些警告:

Because initialize is called in a thread-safe manner and the order of initialize being called on different classes is not guaranteed, it’s important to do the minimum amount of work necessary in initialize methods. Specifically, any code that takes locks that might be required by other classes in their initialize methods is liable to lead to deadlocks. Therefore you should not rely on initialize for complex initialization, and should instead limit it to straightforward, class local initialization.

总结一下,就是这样:

  • 不要在+initialize中处理复杂的逻辑;

那么+initialize可以做些什么事情呢?可以做一些简单的初始化工作,譬如对于某个继承自UICollectionViewCell的自定义类PhotoViewCell,PhotoViewCell的对象可能会有一些公用资源,譬如label color,label font等等,没必要在-initXXOO方法中创建这些完全一样的资源,此时就可以放在PhotoViewCell中的+initialize中完成,如下:

+ (void)initialize {
    titleFont = [UIFont systemFontOfSize:12];
    titleHeight = 20.0f;
    videoIcon = [UIImage imageNamed:@"CTAssetsPickerVideo"];
    titleColor = [UIColor whiteColor];
    checkedIcon = [UIImage imageNamed:@"CTAssetsPickerChecked"];
    selectedColor = [UIColor colorWithWhite:1 alpha:0.3];
}

+initialize终究还是带来惊人的信息量,颇为失望。

+load

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

关于+load方法,《NSObject Class Reference》的介绍如下:

Invoked whenever a class or category is added to the Objective-C runtime; implement this method to perform class-specific behavior upon loading.

关于其使用,《NSObject Class Reference》的说明如下:

The load message is sent to classes and categories that are both dynamically loaded and statically linked, but only if the newly loaded class or category implements a method that can respond.

The order of initialization is as follows:

  1. All initializers in any framework you link to.
  2. All +load methods in your image.
  3. All C++ static initializers and C/C++ attribute(constructor) functions in your image.
  4. All initializers in frameworks that link to you.

In addition:

  • A class’s +load method is called after all of its superclasses’ +load methods.
  • A category +load method is called after the class’s own +load method.
    In a custom implementation of load you can therefore safely message other unrelated classes from the same image, but any load methods implemented by those classes may not have run yet.

从这段文字可以读出如下信息:

  • 在一个程序(main函数)运行之前,所用到的库被加载到runtime之后,被添加到的runtime系统的各种类和category的+load方法就被调用;(关于这点很容易通过打印语句来验证);
  • 如果父类和子类的+load方法都被调用,父类的调用一定在子类之前,这是系统自动完成的,子类+load中没必要显式调用[super load];
  • 文档没有讲明+load的执行是否是线程安全的,但考虑到它是在runtime之前就调用,所以谈论它是否是线程安全没啥必要,根据我的理解,多线程在runtime才有谈论意义;
  • 若某个类由一个主类和多个category组成,则允许主类和category中各自有自己的+load方法,只是category中的+load的执行在主类的+load之后;

关于+load的使用场景,笔者知道的至少有一个,method swizzling的处理一般都在category的+load中完成的,参考这里

参考

    1. 《NSObject Class Reference》;
时间: 2024-10-25 20:17:37

Objective-C中的+initialize和+load的相关文章

iOS中的initialize与load两个类方法简单理解

如果你在一个UIViewController中重写了这两个类方法,那么你会在在这个控制器中发现一下现象: 1,相同之处: (1)这两个类方法在init之前就调用了 (2)在整个应用app中无论你用到这个类多少次,这两个类方法均只会被调用一次 2,不同之处: (1)load是在initialize之前被调用 (2)更特别的是,如果你没有用到你重写的这个控制器.那你重写的load类方法也会调用.换句话说,这个load方法是在didFinishLaunchingWithOptions方法之前就被调用了

[IOS] initialize VS load 大战 3k回合。。

这几天又仔细看了下公司的SDK,对之前用的 load .initialize 又模糊起来了,然后就... YY之后还是要干点活搞点正事呀 ~  T_T ~ +(void)initialize .+(void)load 是NSObject 的两个类方法, NSObject 又作为强大的 root 类, 为其他类提供了一系列的生命周期.线程.内省.运行时.归档等方法. 作为 Root 类也已经 很能干了. 那有啥区别,又该怎么用呢? 1.load 是只要类所在文件被引用就会被调用,而 initial

iOS - + initialize 与 +load

Objective-C 有两个神奇的方法:+load 和 +initialize,这两个方法在类被使用时会自动调用.但是两个方法的不同点会导致应用层面上性能的显著差异. 一.+ initialize 方法和+load 调用时机 首先说一下 + initialize 方法:苹果官方对这个方法有这样的一段描述:这个方法会在 第一次初始化这个类之前 被调用,我们用它来初始化静态变量. load 方法会在加载类的时候就被调用,也就是 ios 应用启动的时候,就会加载所有的类,就会调用每个类的 + loa

iOS-方法之+ initialize 与 +load(转载)

Objective-C 有两个神奇的方法:+load 和 +initialize,这两个方法在类被使用时会自动调用.但是两个方法的不同点会导致应用层面上性能的显著差异. 一.+ initialize 方法和+load 调用时机 首先说一下 + initialize 方法:苹果官方对这个方法有这样的一段描述:这个方法会在 第一次初始化这个类之前 被调用,我们用它来初始化静态变量. load 方法会在加载类的时候就被调用,也就是 ios 应用启动的时候,就会加载所有的类,就会调用每个类的 + loa

Ruby中的require、load、autoload

  require.load.autoload是Kernel模块中定义的方法,由于Class类和Object类都混入了Kernel模块,所以无论self是对象还是类,都可以调用这些方法. 这三个方法都用来加载和执行其他文件,但是有细微的不同,本文将从参数.函数执行.返回值三个方面简要介绍下这三个函数. 1. require(name) -> true or false or raise LoadError http://ruby-doc.org/core-2.1.2/Kernel.html#me

objective C中的字符串(三)

holydancer原创,如需转载,请在显要位置注明: 转自holydancer的CSDN专栏,原文地址:http://blog.csdn.net/holydancer/article/details/7343561 objective C中的字符串操作 在OC中创建字符串时,一般不使用C的方法,因为C将字符串作为字符数组,所以在操作时会有很多不方便的地方,在Cocoa中NSString集成的一些方法,可以很方便的操作字符串,下面举几个例子: 1.创建: 直接利用等号赋值 NSString *

objective C中继承、协议、分类和多态的实现

第一.objective C中继承的实现 在oc中只有实例变量会有权限控制,实例方法和类方法是没有权限控制的,这点与c++不同,OC默认的是protected,并且在声明权限控制时,没有分号 在OC中可以像C++一样用指针运算法来访问实例变量 Rectangle.h 文件代码: #import <Foundation/Foundation.h> @interface Rectangle : NSObject { int _width; int _height; } @property (non

objective C中的字符串

holydancer原创,如需转载,请在显要位置注明: 转自holydancer的CSDN专栏,原文地址:http://blog.csdn.net/holydancer/article/details/7343561 objective C中的字符串操作 在OC中创建字符串时,一般不使用C的方法,因为C将字符串作为字符数组,所以在操作时会有很多不方便的地方,在Cocoa中NSString集成的一些方法,可以很方便的操作字符串,下面举几个例子: 1.创建: 直接利用等号赋值 NSString *

Hibernateibernate中的get()和load()方法

在hibernate中我们知道如果要从数据库中得到一个对象,通常有两种方式,一种是通过session.get()方法,另一种就是通过session.load()方法,然后其实这两种方法在获得一个实体对象时是有区别的,在查询性能上两者是不同的. 一.load加载方式 当使用load方法来得到一个对象时,此时hibernate会使用延迟加载的机制来加载这个对象,即:当我们使用session.load()方法来加载一个对象时,此时并不会发出sql语句,当前得到的这个对象其实是一个代理对象,这个代理对象