专注、坚持

iOS 中的 +initialize 方法

2019.04.21 by kingcos
Date Notes Source Code Demo
2019-04-21 首次提交 objc4-750 -
2019-05-01 补充细节 - Initialize_Demo

Preface

在 iOS 中,经常与 +load 相提并论的一个方法便是 +initialize。从方法名来看,它的作用是进行初始化,但不同于平时使用的 init,+initialize 是一个类方法。那么它的本质到底是什么呢?

为了更好地叙述 +initialize 的特点,本文部分描述尝试与 +load 方法对比,建议阅读本文前先阅读「iOS 中的 +load 方法」。

What

// NSObject.h
@interface NSObject <NSObject> {
+ (void)initialize;
}

Obj-C 中的 +initialize 方法指的是 NSObject 中的 + (void)initialize 类方法。官方文档中的 +initialize 如下:

Initializes the class before it receives its first message.

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. Superclasses receive this message before their subclasses.

The runtime sends the initialize message to classes in a thread-safe manner. That is, initialize is run by the first thread to send a message to a class, and any other thread that tries to send a message to that class will block until initialize completes.

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]. If you want to protect yourself from being run multiple times, you can structure your implementation along these lines:

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

Because initialize is called in a blocking manner, it’s important to limit method implementations to the minimum amount of work necessary possible. 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 is invoked only once per class. If you want to perform independent initialization for the class and for categories of the class, you should implement load methods.

—— Documentation, Apple Developer

译:

在类收到第一条消息前初始化。

在类和所有子类接收第一条消息前,运行时发送 initialize 消息到程序中每个类。父类在子类之前接收到该消息。

运行时以线程安全的方式发送 initialize 消息到类。即 initialize 由第一个线程运行来发送消息到一个类,并且其它任何尝试发送消息的线程都将会被阻塞,直到 initialize 完成。

如果子类未实现 initialize 则父类的实现可能会被调用多次 —— 运行时将会调用继承(自父类)的实现 —— 或者如果子类明确调用 [super initialize]。如果不希望运行多次,可以参考如下实现:

+ (void)initialize {
  if (self == [ClassName self]) {
    // ... do the initialization ...
  }
}

由于 initialize 以阻塞的方式被调用,因此将方法实现限制为最小工作量是很有必要的。尤其是在 initialize 中使用任何可能被其它类所必需锁的代码都有可能会导致死锁。因此不应当依赖 initialize 做复杂的初始化,相反应该将其限制为简单的类本地初始化。

initialize 在每个类中只会被调用一次。如果希望为类以及分类执行独立的初始化,应当实现 load 方法。

—— 文档,苹果开发者

How

根据官方文档的描述,我们可以尝试定义一个继承自 NSObjectPerson 类,并对其添加两个分类 LifeWork;再定义一个 Student 类继承自 Person,并对其添加 School 分类;最后定义一个 Programmer 类继承自 Person。在以上除了 Programmer 的类和分类中,均实现 +initialize:

// Person.m
#import "Person.h"

@implementation Person
+ (void)initialize {
    NSLog(@"Person %s", __func__);
}
@end

// Person+Life.m
#import "Person+Life.h"

@implementation Person (Life)
+ (void)initialize {
    NSLog(@"Person+Life %s", __func__);
}
@end

//  Person+Work.m
#import "Person+Work.h"

@implementation Person (Work)
+ (void)initialize {
    NSLog(@"Person+Work %s", __func__);
}
@end

//  Student.m
#import "Student.h"

@implementation Student
+ (void)initialize {
    NSLog(@"Student %s", __func__);
}
@end

//  Student+School.m
#import "Student+School.h"

@implementation Student (School)
+ (void)initialize {
    NSLog(@"Student+School %s", __func__);
}
@end

//  Programmer.m
#import "Programmer.h"

@implementation Programmer
@end

// OUTPUT:
// Hello, World!

首先我们不更改 main.m,尝试运行程序,结果只输出了 main 函数 Hello, World!,因为 +initialize 并不像 +load 一样是在 main 函数前调用的,而没有输出的原因是这些类还没有收到一条消息。众所周知,Obj-C 中大部分方法调用的本质都是消息发送(Message Sending),[SomeClass classMethod] 是向 SomeClass 类对象发送 classMethod 消息;[SomeObject instanceMethod] 是向 SomeObject 实例对象发送 instanceMethod 方法;由于如果要调用对象方法,也必须要先进行 allocinit(或者 new)才能创建完一个实例对象,因此可以认为类接收到的第一条消息永远是类方法。这次我们尝试向它们发送消息:

// main.m
#import <Foundation/Foundation.h>

#import "Person.h"
#import "Student.h"
#import "Programmer.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSLog(@"---");
        Student *stu = [Student alloc];
        NSLog(@"---");
        stu = [stu init];

        NSLog(@"---");
        [Programmer load];
    }
    return 0;
}


// OUTPUT:
// ---
// Person+Work +[Person(Work) initialize]
// Student+School +[Student(School) initialize]
// ---
// ---
// Person+Work +[Person(Work) initialize]

通过以上代码的输出,虽然我们并没有主动调用这些类的 +initialize 方法,但 +initialize 确实被自动调用了。我们先抛出结论:+initialize 方法在类(类对象)接收到第一条消息(类方法)前被调用;子类中如果实现了该方法,会先父类再子类;且遵守分类(Category)「覆盖」主类的 +initialize;若子类未实现 +initialize,将调用父类中的该方法,因此一个类中的 +initialize 并不能保证只被调用一次,但对于某个类,只会被调用一次

Why

实现原理

上面我们先抛出了结论,那么该如何验证呢?Talk is cheap, show me the code:

// objc-class.mm
/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
* 为指定的类和方法选择器返回相应的类方法。
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    // ➡️ 返回元类的方法
    // 因为类对象和元类对象的结构一致(均为 Class),因此元类的类方法和类的对象方法存储在同样的结构中
    return class_getInstanceMethod(cls->getMeta(), sel);
}

// objc-runtime-new.mm
/***********************************************************************
* class_getInstanceMethod.  Return the instance method for the
* specified class and selector.
* 为指定的类和方法选择器返回相应的对象方法。
**********************************************************************/
Method class_getInstanceMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    // This deliberately avoids +initialize because it historically did so.
    // 由于历史原因,这里故意避免 +initialize。

    // This implementation is a bit weird because it's the only place that
    // wants a Method instead of an IMP.
    // 这里的实现有点奇怪,因为这是唯一需要 Method 而非 IMP 的地方。

#warning fixme build and search caches

    // Search method lists, try method resolver, etc.
    // 搜索方法列表,尝试方法解析器等。
    // ➡️ 查找 IMP
    lookUpImpOrNil(cls, sel, nil,
                   NO/*initialize*/, NO/*cache*/, YES/*resolver*/);

#warning fixme build and search caches

    return _class_getMethod(cls, sel);
}

// objc-runtime-new.mm
/***********************************************************************
* lookUpImpOrNil.
* Like lookUpImpOrForward, but returns nil instead of _objc_msgForward_impcache
* 类似 lookUpImpOrForward,但返回 nil 替代 _objc_msgForward_impcache
**********************************************************************/
IMP lookUpImpOrNil(Class cls, SEL sel, id inst,
                   bool initialize, bool cache, bool resolver)
{
    // ➡️ 查找 IMP
    IMP imp = lookUpImpOrForward(cls, sel, inst, initialize, cache, resolver);
    // 当 IMP 为 _objc_msgForward_impcache 时,返回 nil
    if (imp == _objc_msgForward_impcache) return nil;
    else return imp;
}

// objc-runtime-new.mm
/***********************************************************************
* lookUpImpOrForward.
* The standard IMP lookup.
* 标准 IMP 查找。
* initialize==NO tries to avoid +initialize (but sometimes fails)
* initialize==NO 尝试避免 +initialize(但有时会失败)
* cache==NO skips optimistic unlocked lookup (but uses cache elsewhere)
* cache==NO 跳过乐观解锁查找(但有时仍会使用缓存)
* Most callers should use initialize==YES and cache==YES.
* 大多数调用者应当使用 initialize==YES 且 cache==YES。
* inst is an instance of cls or a subclass thereof, or nil if none is known.
* inst 是 cls 或其子类,如果没有,则为 nil。
*   If cls is an un-initialized metaclass then a non-nil inst is faster.
*   如果 cls 是未初始化的元类,那么非 nil 的 inst 更快。
* May return _objc_msgForward_impcache. IMPs destined for external use
*   must be converted to _objc_msgForward or _objc_msgForward_stret.
* 可能返回 _objc_msgForward_impcache。外部使用 IMP 则注定必须被转换为 _objc_msgForward 或 _objc_msgForward_stret。
*   If you don't want forwarding at all, use lookUpImpOrNil() instead.
*   如果根本不需要转发,使用 lookUpImpOrNil() 替代。
**********************************************************************/
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    // 断言解锁
    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    // 乐观缓存查找
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    // runtimeLock is held during isRealized and isInitialized checking
    // to prevent races against concurrent realization.
    // 在 isRealized 和 isInitialized 检查期间保持 runtimeLock,防止并发实化竞争。

    // runtimeLock is held during method search to make
    // method-lookup + cache-fill atomic with respect to method addition.
    // 关于方法添加,在方法搜索期间保持 runtimeLock,使得方法查找和缓存填充原子化(不可再分割)。
    // Otherwise, a category could be added but ignored indefinitely because
    // the cache was re-filled with the old value after the cache flush on
    // behalf of the category.
    // 否则,分类可以被添加但总会被忽略,因为分类的缓存在刷新后会被旧值重复填充。

    runtimeLock.lock();
    checkIsKnownClass(cls);

    // 如果未实化,进行实化
    if (!cls->isRealized()) {
        realizeClass(cls);
    }

    // 如果需要初始化但未初始化,进行初始化
    if (initialize  &&  !cls->isInitialized()) {
        runtimeLock.unlock();
        // ➡️ 类初始化
        _class_initialize (_class_getNonMetaClass(cls, inst));
        runtimeLock.lock();
        // If sel == initialize, _class_initialize will send +initialize and
        // then the messenger will send +initialize again after this
        // procedure finishes. Of course, if this is not being called
        // from the messenger then it won't happen. 2778172
        // 如果 sel 等于 initialize,_class_initialize 将发送 +initialize 消息,
        // 之后发送者将会在该过程完成后再次发送 +initialize。当然,如果不是发送者调用,则不会发生。
    }


 retry:
    runtimeLock.assertLocked();

    // Try this class's cache.

    imp = cache_getImp(cls, sel);
    if (imp) goto done;

    // Try this class's method lists.
    {
        Method meth = getMethodNoSuper_nolock(cls, sel);
        if (meth) {
            log_and_fill_cache(cls, meth->imp, sel, inst, cls);
            imp = meth->imp;
            goto done;
        }
    }

    // Try superclass caches and method lists.
    {
        unsigned attempts = unreasonableClassCount();
        for (Class curClass = cls->superclass;
             curClass != nil;
             curClass = curClass->superclass)
        {
            // Halt if there is a cycle in the superclass chain.
            if (--attempts == 0) {
                _objc_fatal("Memory corruption in class list.");
            }

            // Superclass cache.
            imp = cache_getImp(curClass, sel);
            if (imp) {
                if (imp != (IMP)_objc_msgForward_impcache) {
                    // Found the method in a superclass. Cache it in this class.
                    log_and_fill_cache(cls, imp, sel, inst, curClass);
                    goto done;
                }
                else {
                    // Found a forward:: entry in a superclass.
                    // Stop searching, but don't cache yet; call method
                    // resolver for this class first.
                    break;
                }
            }

            // Superclass method list.
            Method meth = getMethodNoSuper_nolock(curClass, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                imp = meth->imp;
                goto done;
            }
        }
    }

    // No implementation found. Try method resolver once.

    if (resolver  &&  !triedResolver) {
        runtimeLock.unlock();
        _class_resolveMethod(cls, sel, inst);
        runtimeLock.lock();
        // Don't cache the result; we don't hold the lock so it may have
        // changed already. Re-do the search from scratch instead.
        triedResolver = YES;
        goto retry;
    }

    // No implementation found, and method resolver didn't help.
    // Use forwarding.

    imp = (IMP)_objc_msgForward_impcache;
    cache_fill(cls, sel, imp, inst);

 done:
    runtimeLock.unlock();

    return imp;
}

// objc-initialize.mm
/***********************************************************************
* class_initialize.  Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
* class_initialize。按需发送「+initialize」消息到所有未初始化的类。强制父类最先初始化。
**********************************************************************/
void _class_initialize(Class cls)
{
    // 断言非元类
    assert(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // 在开始初始化 cls 前确保父类已经完成初始化。
    // See note about deadlock above.
    // 查看上面死锁的备注。
    // 取得父类
    supercls = cls->superclass;
    // 如果父类存在,且未初始化
    if (supercls  &&  !supercls->isInitialized()) {
    	 // 递归初始化父类
        _class_initialize(supercls);
    }

    // Try to atomically set CLS_INITIALIZING.
    // 原子化地设置 CLS_INITIALIZING。
    {
        monitor_locker_t lock(classInitLock);
        if (!cls->isInitialized() && !cls->isInitializing()) {
            cls->setInitializing();
            reallyInitialize = YES;
        }
    }

    if (reallyInitialize) {
        // We successfully set the CLS_INITIALIZING bit. Initialize the class.
        // 成功设置 CLS_INITIALIZING 位。初始化类。

        // Record that we're initializing this class so we can message it.
        // 记录我们正在初始化该类,以便可以发送这个消息。
        _setThisThreadIsInitializingClass(cls);

        if (MultithreadedForkChild) {
            // LOL JK we don't really call +initialize methods after fork().
            performForkChildInitialize(cls, supercls);
            return;
        }

        // Send the +initialize message.
        // 发送 +initialize 消息。
        // Note that +initialize is sent to the superclass (again) if
        // this class doesn't implement +initialize. 2157218
        // 如果该类未实现 +initialize,需要注意 +initialize(再次)被发送到了父类
        // Xcode 中 OBJC_PRINT_INITIALIZE_METHODS 环境变量值为 YES 时,将可在控制台打印该信息
        // OPTION(PrintInitializing, OBJC_PRINT_INITIALIZE_METHODS, "log calls to class +initialize methods")
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: thread %p: calling +[%s initialize]",
                         pthread_self(), cls->nameForLogging());
        }

        // Exceptions: A +initialize call that throws an exception
        // is deemed to be a complete and successful +initialize.
        //
        // Only __OBJC2__ adds these handlers. !__OBJC2__ has a
        // bootstrapping problem of this versus CF's call to
        // objc_exception_set_functions().
#if __OBJC2__
        @try
#endif
        {
            // ➡️ 调用 +initialize
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                             pthread_self(), cls->nameForLogging());
            }
        }
#if __OBJC2__
        @catch (...) {
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
                             "threw an exception",
                             pthread_self(), cls->nameForLogging());
            }
            @throw;
        }
        @finally
#endif
        {
            // Done initializing.
            lockAndFinishInitializing(cls, supercls);
        }
        return;
    }

    else if (cls->isInitializing()) {
        // We couldn't set INITIALIZING because INITIALIZING was already set.
        // If this thread set it earlier, continue normally.
        // If some other thread set it, block until initialize is done.
        // It's ok if INITIALIZING changes to INITIALIZED while we're here,
        //   because we safely check for INITIALIZED inside the lock
        //   before blocking.
        if (_thisThreadIsInitializingClass(cls)) {
            return;
        } else if (!MultithreadedForkChild) {
            waitForInitializeToComplete(cls);
            return;
        } else {
            // We're on the child side of fork(), facing a class that
            // was initializing by some other thread when fork() was called.
            _setThisThreadIsInitializingClass(cls);
            performForkChildInitialize(cls, supercls);
        }
    }

    else if (cls->isInitialized()) {
        // Set CLS_INITIALIZING failed because someone else already
        //   initialized the class. Continue normally.
        // NOTE this check must come AFTER the ISINITIALIZING case.
        // Otherwise: Another thread is initializing this class. ISINITIALIZED
        //   is false. Skip this clause. Then the other thread finishes
        //   initialization and sets INITIALIZING=no and INITIALIZED=yes.
        //   Skip the ISINITIALIZING clause. Die horribly.
        return;
    }

    else {
        // We shouldn't be here.
        _objc_fatal("thread-safe class init in objc runtime is buggy!");
    }
}

// objc-initialize.mm
void callInitialize(Class cls)
{
    // 💡 向 cls 发送 SEL_initialize 消息
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

追根溯源,我们可以看到 +initialize 的本质其实是 objc_msgSend,即消息发送机制,不同于 +load 直接根据 IMP 指针进行调用。这也就解释了在 Demo 中,我们首先尝试向 Student 类发送一个 alloc 消息,运行时会先找到父类 Person 的 +initialize 方法进行调用,而由于父类又存在分类,最后编译的 Person+Work 分类「覆盖」了其它分类和主类中相同的方法而得以调用;随后调用子类自身的 +initialize 方法,而 Student+School 分类「覆盖」了主类中相同的方法而得以调用;而向 Programmer 类手动发送 +load 消息时(当然,一般我们不会这么做),由于该类本身没有实现 +initialize 方法,因此最终通过父类的 Person+Work 分类调用了 +initialize 方法。

如上所述,一个类的 +initialize 方法并不能保证只会被调用一次,所以如果我们不希望多次调用,可以在 +initialize 方法中进行判断 self == [ClassName self]

OBJC_PRINT_INITIALIZE_METHODS

为了便于 Debug 运行时的 +initialize 方法,将 OBJC_PRINT_INITIALIZE_METHODS 在 Xcode 中设置为 YES 后,可以打印出很多 + initialize 方法执行时的信息:

objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSObject initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSObject initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSObject is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_dispatch_data initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_dispatch_data initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_dispatch_data is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_object initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_object initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_object is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_object initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_object initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_object is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_string initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_string initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_string is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_data initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_data initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_data is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_dictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_dictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_dictionary is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_serializer initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_serializer initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_serializer is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_dispatch_object initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_dispatch_object initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_dispatch_object is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_dispatch_mach_msg initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_dispatch_mach_msg initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_dispatch_mach_msg is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_array initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_array initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_array is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSCFType initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSCFType initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSCFType is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSCFString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSCFString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSCFString is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSArray is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSMutableArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSMutableArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSMutableArray is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSPlaceholderArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSPlaceholderArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSPlaceholderArray is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSSingleObjectArrayI initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSSingleObjectArrayI initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSSingleObjectArrayI is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSDictionary is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSDictionary0 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSDictionary0 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSDictionary0 is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSMutableDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSMutableDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSMutableDictionary is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSArray0 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSArray0 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSArray0 is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSEnumerator initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSEnumerator initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSEnumerator is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSEnumerator0 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSEnumerator0 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSEnumerator0 is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSLock initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSLock initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSLock is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSRecursiveLock initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSRecursiveLock initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSRecursiveLock is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSThread initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSThread initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSThread is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSCFDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSCFDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSCFDictionary is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[_NSThreadData initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[_NSThreadData initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: _NSThreadData is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSPlaceholderDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSPlaceholderDictionary initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSPlaceholderDictionary is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSDictionaryM initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSDictionaryM initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSDictionaryM is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSCFArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSCFArray initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSCFArray is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSSet initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSSet initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSSet is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSMutableSet initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSMutableSet initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSMutableSet is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSPlaceholderSet initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSSetI initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSSetI initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSSetI is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSPlaceholderSet initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSPlaceholderSet is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSSetM initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSSetM initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSSetM is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSBindingSelectionMarker initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSBindingSelectionMarker initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSBindingSelectionMarker is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[_NSStateMarker initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[_NSStateMarker initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: _NSStateMarker is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSCFConstantString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSPlaceholderString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSPlaceholderString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: class NSPlaceholderString will be marked as fully +initialized after superclass +[NSString initialize] completes
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSTaggedPointerString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSTaggedPointerString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: class NSTaggedPointerString will be marked as fully +initialized after superclass +[NSString initialize] completes
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSString is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSTaggedPointerString is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSPlaceholderString is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSMutableString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSPlaceholderMutableString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSPlaceholderMutableString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: class NSPlaceholderMutableString will be marked as fully +initialized after superclass +[NSMutableString initialize] completes
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSMutableString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSMutableString is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSPlaceholderMutableString is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSCFConstantString initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSCFConstantString is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSBundle initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSBundle initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSBundle is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[_CFXPreferencesHandle initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[_CFXPreferencesHandle initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: _CFXPreferencesHandle is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[_CFXPreferences initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[_CFXPreferences initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: _CFXPreferences is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[NSURL initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[NSURL initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: NSURL is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[CFPrefsSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[CFPrefsSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: CFPrefsSource is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[CFPrefsSearchListSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[CFPrefsSearchListSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: CFPrefsSearchListSource is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[CFPrefsPlistSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[CFPrefsPlistSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: CFPrefsPlistSource is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[CFPrefsManagedSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[CFPrefsManagedSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: CFPrefsManagedSource is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[__NSArrayM initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[__NSArrayM initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: __NSArrayM is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_mach_send initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_mach_send initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_mach_send is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_uuid initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_uuid initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_uuid is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_xpc_int64 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_xpc_int64 initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_xpc_int64 is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[OS_voucher initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[OS_voucher initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: OS_voucher is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[CFPrefsSuiteSearchListSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[CFPrefsSuiteSearchListSource initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: CFPrefsSuiteSearchListSource is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[NSData initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: blocking until +[NSData initialize] completes
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[NSData initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: NSData is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[NSMutableData initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: blocking until +[NSMutableData initialize] completes
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[NSMutableData initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: NSMutableData is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[__NSCFData initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: blocking until +[__NSCFData initialize] completes
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[__NSCFData initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: __NSCFData is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[_NSDispatchData initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[_NSDispatchData initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: _NSDispatchData is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[NSDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[NSDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: NSDate is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[__NSPlaceholderDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[__NSPlaceholderDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: __NSPlaceholderDate is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[__NSTaggedDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[__NSTaggedDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: __NSTaggedDate is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[__NSDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[__NSDate initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: __NSDate is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[NSValue initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[NSNumber initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[NSNumber initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: class NSNumber will be marked as fully +initialized after superclass +[NSValue initialize] completes
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[NSPlaceholderValue initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[NSPlaceholderValue initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: class NSPlaceholderValue will be marked as fully +initialized after superclass +[NSNumber initialize] completes
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[NSPlaceholderNumber initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[NSPlaceholderNumber initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: class NSPlaceholderNumber will be marked as fully +initialized after superclass +[NSPlaceholderValue initialize] completes
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[NSValue initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: NSValue is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: NSNumber is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: NSPlaceholderValue is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: NSPlaceholderNumber is fully +initialized
objc[23971]: INITIALIZE: thread 0x700009500000: calling +[__NSCFNumber initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: finished +[__NSCFNumber initialize]
objc[23971]: INITIALIZE: thread 0x700009500000: __NSCFNumber is fully +initialized
2019-05-01 00:07:04.813693+0800 Initialize_Demo[23971:21942131] ---
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[Person initialize]
2019-05-01 00:07:04.814048+0800 Initialize_Demo[23971:21942131] Person+Work +[Person(Work) initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[Person initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: Person is fully +initialized
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[Student initialize]
2019-05-01 00:07:04.814168+0800 Initialize_Demo[23971:21942131] Student+School +[Student(School) initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[Student initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: Student is fully +initialized
2019-05-01 00:07:04.814271+0800 Initialize_Demo[23971:21942131] ---
2019-05-01 00:07:04.814297+0800 Initialize_Demo[23971:21942131] ---
objc[23971]: INITIALIZE: thread 0x1000aa5c0: calling +[Programmer initialize]
2019-05-01 00:07:04.814347+0800 Initialize_Demo[23971:21942131] Person+Work +[Person(Work) initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: finished +[Programmer initialize]
objc[23971]: INITIALIZE: thread 0x1000aa5c0: Programmer is fully +initialized

开销

+initialize 方法不同于 +load 方法,不会使得程序将时间过多地浪费在启动过程中,因此我们可以将一些合适的操作下沉到 +initialize 中完成。但 +initialize 是以阻塞的方式被调用,也不适合做特别多繁重尤其是可能导致死锁的操作。

Reference