【iOS】Initialize

947 阅读4分钟

源码解读

在objc-runtime-new.mm中找到class_getInstanceMethod方法

Method class_getInstanceMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    // This deliberately avoids +initialize because it historically did so.

    // This implementation is a bit weird because it's the only place that 
    // wants a Method instead of an IMP.

#warning fixme build and search caches
        
    // Search method lists, try method resolver, etc.
    lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);

#warning fixme build and search caches

    return _class_getMethod(cls, sel);
}

进入lookUpImpOrNil方法

lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)
{
    return lookUpImpOrForward(obj, sel, cls, behavior | LOOKUP_CACHE | LOOKUP_NIL);
}

进入lookUpImpOrForward方法 条件!cls->isInitialized() 类未初始化

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    ...
    
    // 重点 isInitialized
    if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        // runtimeLock may have been dropped but is now locked again

        // 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
    }

    ...
}

进入initializeAndLeaveLocked

// Locking: caller must hold runtimeLock; this may drop and re-acquire it
static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
{
    return initializeAndMaybeRelock(cls, obj, lock, true);
}

进入initializeAndMaybeRelock方法

/***********************************************************************
* class_initialize.  Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
* inst is an instance of cls, or nil. Non-nil is better for performance.
* Returns the class pointer. If the class was unrealized then 
* it may be reallocated.
* Locking: 
*   runtimeLock must be held by the caller
*   This function may drop the lock.
*   On exit the lock is re-acquired or dropped as requested by leaveLocked.
**********************************************************************/
static Class initializeAndMaybeRelock(Class cls, id inst,
                                      mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();
    ASSERT(cls->isRealized());

    if (cls->isInitialized()) {
        if (!leaveLocked) lock.unlock();
        return cls;
    }

    // Find the non-meta class for cls, if it is not already one.
    // The +initialize message is sent to the non-meta class object.
    Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);

    // Realize the non-meta class if necessary.
    if (nonmeta->isRealized()) {
        // nonmeta is cls, which was already realized
        // OR nonmeta is distinct, but is already realized
        // - nothing else to do
        lock.unlock();
    } else {
        nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
        // runtimeLock is now unlocked
        // fixme Swift can't relocate the class today,
        // but someday it will:
        cls = object_getClass(nonmeta);
    }

    // runtimeLock is now unlocked, for +initialize dispatch
    ASSERT(nonmeta->isRealized());
    // 初始化
    initializeNonMetaClass(nonmeta);

    if (leaveLocked) runtimeLock.lock();
    return cls;
}

进入initializeNonMetaClass方法 递归操作,父类未初始化,先调用父类

/***********************************************************************
* class_initialize.  Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/

void initializeNonMetaClass(Class cls)
{
    ASSERT(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    supercls = cls->superclass;
    // 递归操作 所以会无限调用父类
    if (supercls  &&  !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }
    
     // 调用初始化方法
     callInitialize(cls);
}

进入callInitialize方法 initialize走的是class_msgSend的消息转发方法

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));
    asm("");
}

例子

期望结果:

  • 子类未初始化initilize的时候会调用父类的initilize,不同子类会有可能都调用,但真正初始化只有第一次
  • initialize的源码回归操作说明先父类如果未初始化先调用父类
  • initialize走的是class_msgSend的消息转发方法,所以会先调用分类的方法,而且先编译后调用

MJStudent,MJTeacher的父类为MJPerson,这里MJPerson实现initialize方法

@implementation MJPerson

+ (void)initialize
{
    NSLog(@"MJPerson +initialize");
}

@end

@implementation MJStudent
@end

@implementation MJTeacher
@end

[MJStudent alloc]; // MJStudent首先初始父类的initialize方法,MJStudent未实现initialize方法,则调用父类
[MJTeacher alloc]; MJTeacher未实现initialize方法,则调用父类

打印结果:

-load[15503:456856] MJPerson +initialize
-load[15503:456856] MJPerson +initialize
-load[15503:456856] MJPerson +initialize

给MJStudent, MJTeacher添加initialize方法

@implementation MJStudent
+ (void)initialize
{
    NSLog(@"MJStudent +initialize");
}
@end

@implementation MJTeacher
+ (void)initialize
{
    NSLog(@"MJTeacher +initialize");
}
@end
  [MJStudent alloc];  // 会先调用MJPerson的initialize,后调用MJStudent的initialize
  [MJTeacher alloc];  // MJPerson已经initialize,只调用MJTeacher的initialize
  [MJPerson alloc];   // MJPerson已经initialize
  

打印结果:

-load[15297:447570] MJPerson +initialize
-load[15297:447570] MJStudent +initialize
-load[15297:447570] MJTeacher +initialize

MJStudent分类添加Test1,并且实现initialize方法

@implementation MJStudent(Test1)
+ (void)initialize
{
    NSLog(@"MJStudent (Test1) +initialize");
}
@end 
  [MJStudent alloc];  // 会先调用MJPerson的initialize,后调用MJStudent分类的initialize
  [MJTeacher alloc];  // MJPerson已经initialize,只调用MJTeacher的initialize
  [MJPerson alloc];   // MJPerson已经initialize

打印结果:

-load[15331:449685] MJPerson +initialize
-load[15331:449685] MJStudent (Test1) +initialize
-load[15331:449685] MJTeacher +initialize

总结

  • +initialize方法会在类第一次接收到消息时调用

  • 调用顺序,先调用父类,再调用子类,有分类先调用分类

  • 源码解读顺序

    1.objc-runtime-new.mm

    2.class_getInstanceMethod

    3.lookUpImpOrForward

    4._class_initialize

    5.callInitialize

    6.objc_msgSend(cls, SEL_initialize)

  • load和initlize的区别

    1.调用方式

    load通过函数地址直接调用

    initialize通过objc_msgSend调用

    2.调用时刻

    load是runtime加载类,分类时候调用(只会调用一次)

    initialize是类第一次接收到消息时候调用,每个类只会initialize一次(分类的initialize可能会被调用多次)

    3.调用顺序

    load:

    1.先调用类的load(先编译先调用,调用子类的load之前会先调用父类的load)

    2.再调用分类的load(先编译先调用)

    initialize:先初始化父类,再初始化子类(可能最终调用的是父类的initialize方法)