IOS 拾遗

IOS initialize 和 load 学习笔记

2017-06-07  本文已影响57人  madaoCN
oc绝大多数类都继承自根类NSObject该类有两个初始化方法,一个是initialize另一个则是load

对于加入运行期系统中的每个Class或者Category,必定会调用此方法,并且只会调用一次。load方法执行时,runtime系统处于“脆弱状态”。如果load方法中使用了其他类,那么相关类的load方法必定会得到执行。然而,根据指定的库,却无法判断各个类的加载顺序。所以,在load方法里使用其他类是不安全的。

#import "ClassA.h"
#import "ClassB.h"

@implementation ClassA

+ (void)load
{
    ClassB *obj = [ClassB new];//无法确定ClassB是否已经载入
    
}
@end

遗憾的是NSObject的源码并没有什么东西

// NSObject.mm
@implementation NSObject

+ (void)load {
}

所有我们按照这前一段代码,来验证load方法的一些行为,我们在ClassB *obj = [ClassB new];之后打下断点,查看程序的调用栈

调用栈.png

可以发现[ClassB load]之前调用了load_imagescall_load_methods这两个私有方法。

load_images方法位于源码objc-runtime-new.mm文件中

void
load_images(const char *path __unused, const struct mach_header *mh)
{
  
    // (1)如果没有实现+load方法,直接return,
    //   所以如果父类实现了load方法,此处return之后也不会被调用
    // Return without taking locks if there are no +load methods here. 
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // (2)递归查找所有已导入的父类和分类的load方法(如果实现了load方法的话)此处代码略长,有兴趣可以深究看看
    {
        rwlock_writer_t lock2(runtimeLock);
        prepare_load_methods((const headerType *)mh);
    }
  // 调用+load 方法
    call_load_methods();
}

call_load_methods方法位于源码 objc-loadmethod.h
文件中

void call_load_methods(void)
{
    //.....此处代码省略
    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }
        // 2. 调用一次category分类中的+loads 方法
        more_categories = call_category_loads();
        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}

call_load_methods中调用了其他方法,就不深究下去了
但是仍然可见:

  1. 如果类本身没有实现load方法,那么不管父类是否实现了load方法,那么都不会调用(见load_images中(1)语句)
  1. 执行子类load方法之前,会执行所有父类的load方法(见load_images中(2)语句)
  2. category中的如果定义了load方法,那么也会调用(见call_load_methods2语句)
  3. 类中的load方法比分类中的load方法先调用,call_load_methods方法定义中 先执行call_class_loads ()语句再执行call_category_loads ()

因为load在类载入时就会调用,如果写了过多耗时操作,就会导致程序阻塞无响应,但是它适合用在Category中判断类是否已经加载。

如果真正想执行类的初始化操作,可以实现initialize方法,与load区别在于,initialize是懒加载的,如果某个类没有被使用那么,initialize方法永远也不会被调用,也就是说,不必像load方法那样要执行每一个类的load方法,程序才能继续执行下去。

  1. 父类若实现了initialize方法,即使子类没有实现,那么也会调用父类的initialize方法
// SuperClass
#import "SuperClass.h"
@interface SuperClass()
@end
@implementation SuperClass

+ (void)initialize
{
    NSLog(@"Super initialize");
}
@end
// SubClass
@interface SubClass()
@end
@implementation SubClass

//+ (void)initialize
//{
//    NSLog(@"SubClass initialize");
//}
@end
示例程序

可见即使子类未实现initialize方法父类也会调用initialize方法

接下来让我们关心下源码。残念,NSObject.mm也是一如既往的空空如也

@implementation NSObject

+ (void)load {
}
+ (void)initialize {
}

如法炮制,我们在superclass处打下断点

superclass

我们发现调用了_class_initialize私有方法,源码略长略长


/***********************************************************************
* class_initialize.  Send the '+initialize' message on demand to any
* uninitialized class. Force initialization of superclasses first.
**********************************************************************/
void _class_initialize(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()) {
        _class_initialize(supercls);
    }
    
    // Try to atomically set 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.
        
        // Record that we're initializing this class so we can message it.
        _setThisThreadIsInitializingClass(cls);
        
        // Send the +initialize message.
        // Note that +initialize is sent to the superclass (again) if 
        // this class doesn't implement +initialize. 2157218
        if (PrintInitializing) {
            _objc_inform("INITIALIZE: calling +[%s initialize]",
                         cls->nameForLogging());
        }

        // Exceptions: A +initialize call that throws an exception 
        // is deemed to be a complete and successful +initialize.
        @try {
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: finished +[%s initialize]",
                             cls->nameForLogging());
            }
        }
        @catch (...) {
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: +[%s initialize] threw an exception",
                             cls->nameForLogging());
            }
            @throw;
        }
        @finally {
            // Done initializing. 
            // If the superclass is also done initializing, then update 
            //   the info bits and notify waiting threads.
            // If not, update them later. (This can happen if this +initialize 
            //   was itself triggered from inside a superclass +initialize.)
            monitor_locker_t lock(classInitLock);
            if (!supercls  ||  supercls->isInitialized()) {
                _finishInitializing(cls, supercls);
            } else {
                _finishInitializingAfter(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 {
            waitForInitializeToComplete(cls);
            return;
        }
    }
    
    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!");
    }
}

有点困了😴😴,有空再来填坑吧

上一篇下一篇

猜你喜欢

热点阅读