随笔--iOS load initialize

一、为何源类+load先执行 源类分类没有覆盖+load方法?

那得看源码了看objc-os中的_objc_init方法中

void _objc_init(void)

{

    static bool initialized = false;

    if (initialized) return;

    initialized = true;

    

    // fixme defer initialization until an objc-using image is found?

    environ_init();

    tls_init();

    static_init();

    runtime_init();

    exception_init();

    cache_init();

    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);

 

#if __OBJC2__

    didCallDyldNotifyRegister = true;

#endif

}

_dyld_objc_notify_register(&map_images, load_images, unmap_image); 在objc-runtime-new找到load_images方法

void

load_images(const char *path __unused, const struct mach_header *mh)

{

    if (!didInitialAttachCategories && didCallDyldNotifyRegister) {

        didInitialAttachCategories = true;

        loadAllCategories();

    }

    // Return without taking locks if there are no +load methods here.

    if (!hasLoadMethods((const headerType *)mh)) return;

 

    recursive_mutex_locker_t lock(loadMethodLock);

 

    // Discover load methods

    {

        mutex_locker_t lock2(runtimeLock);

        prepare_load_methods((const headerType *)mh);

    }

    // Call +load methods (without runtimeLock - re-entrant)

    call_load_methods();

}

 

查看call_methods方法 objc-loadmethod文件中看到

开启一个自动释放池来访问类中的+loads方法,第二步才调用分类方法,直到没有跟多类或者分类方法

void call_load_methods(void)

{

    static bool loading = NO;

    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.

    if (loading) return;

    loading = YES;

    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. Call category +loads ONCE

        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_class_loads函数实现

static void call_class_loads(void){

    int i;

    // Detach current loadable list.

    struct loadable_class *classes = loadable_classes;

    int used = loadable_classes_used;

    loadable_classes = nil;

    loadable_classes_allocated = 0;

    loadable_classes_used = 0;

    // Call all +loads for the detached list.

    for (i = 0; i < used; i++) {

        Class cls = classes[i].cls;

        load_method_t load_method = (load_method_t)classes[i].method;

        if (!cls) continue;

        if (PrintLoading) {

            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());

        }

        (*load_method)(cls, @selector(load));

    }

    // Destroy the detached list.

    if (classes) free(classes);

}

拿到load_method 函数指针直接调用load函数

  load_method_t load_method = (load_method_t)classes[i].method;

    (*load_method)(cls, @selector(load));

 

总结

load调用和编译顺序是没有关系的,因为在_objc_init---load_images---call_load_methods----call_class_loads中

load_method_t load_method = (load_method_t)classes[i].method;

(*load_method)(cls, @selector(load));

分类为何会覆盖类中的方法,因为在它是通过消息机制进传递,类对象会通过isa指针找到元类中的方法列表进行查询。

二、initialize方法

001 initialize方法会在类第一次接收到消息时调用。走的是objc_msgSend调用。

调用顺序:先调用父类的+initialize,再调用子类的initialize。

 

[MyObj alloc] ---->objc_msgSend([MyObj class],@selector(alloc))

isa找到类对象/元类对象,寻找方法调用;superclass找到类对象/元类对象寻找方法;

 

看源码

objc-runtime-new文件下

/***********************************************************************

* 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.

 

    // 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);

}

 

objc-initialize.mm

void callInitialize(Class cls)

{

    ((void(*)(Class, SEL))objc_msgSend)(cls, @selector(initialize));

    asm("");

}

核心代码

// Try to atomically set CLS_INITIALIZING.

    SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;

    {

        monitor_locker_t lock(classInitLock);

        if (!cls->isInitialized() && !cls->isInitializing()) {

            cls->setInitializing();

            reallyInitialize = YES;

            // Grab a copy of the will-initialize funcs with the lock held.

            localWillInitializeFuncs.initFrom(willInitializeFuncs);

        }

    }

步骤:

先确定superclass调用initialize,

接下来就是在当前类调用initialize

/***********************************************************************

* 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);

    }

    

    // Try to atomically set CLS_INITIALIZING.

    SmallVector<_objc_willInitializeClassCallback, 1> localWillInitializeFuncs;

    {

        monitor_locker_t lock(classInitLock);

        if (!cls->isInitialized() && !cls->isInitializing()) {

            cls->setInitializing();

            reallyInitialize = YES;

 

            // Grab a copy of the will-initialize funcs with the lock held.

            localWillInitializeFuncs.initFrom(willInitializeFuncs);

        }

    }

    

    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);

 

        if (MultithreadedForkChild) {

            // LOL JK we don't really call +initialize methods after fork().

            performForkChildInitialize(cls, supercls);

            return;

        }

        

        for (auto callback : localWillInitializeFuncs)

            callback.f(callback.context, 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: thread %p: calling +[%s initialize]",

                         objc_thread_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

        {

            callInitialize(cls);

 

            if (PrintInitializing) {

                _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",

                             objc_thread_self(), cls->nameForLogging());

            }

        }

#if __OBJC2__

        @catch (...) {

            if (PrintInitializing) {

                _objc_inform("INITIALIZE: thread %p: +[%s initialize] "

                             "threw an exception",

                             objc_thread_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!");

    }

}

总结:

+ initializehe 和+load最大的区别在于 +initializehe是通过objc_msgSend()进行调用

如果子类没有实现+initialize,会调用父类的+initialize,所以父类会被多次调用

如果分类中实现了+initialize就覆盖类本身的+initialize

 

 

    

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值