美文网首页
initialize被调用次数探究

initialize被调用次数探究

作者: 传说中的汽水枪 | 来源:发表于2019-01-03 18:39 被阅读11次

概况

我们都知道+initialize方法会在此类第一次被使用的时候会被调用,那么调用的次数是靠什么来决定的了?
结论一:类第一次被使用的时候,会先调用+initialize方法

NSObject关于initialize源码

源码地址: https://github.com/RetVal/objc-runtime.git

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

        if (MultithreadedForkChild) {
            // LOL JK we don't really call +initialize methods after fork().
            performForkChildInitialize(cls, supercls);
            return;
        }
        
        // 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]",
                         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
        {
            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!");
    }
}

先不考虑加锁和多线程问题,_class_initialize主要的就是如下两个步骤:

  1. 如果父类没有初始化,那么递归初始化父类
  2. 初始化自己

第二步会调用:

void callInitialize(Class cls)
{
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}

看到objc_msgSend表明:
结论二:会走OC的消息发送流程

根据以上的推论我们可以得知:
+initialize会被调用0次,一次,多次

  • 调用0次:表明这个类没有被使用到
  • 调用1次:表明只有这个类被使用到,它的子类要不然是没有被使用到,要不然就是有这个子类自己的+initialize(子类中+initialize中没有[super initialize]代码)方法
  • 调用多次:表明这个类或者它的多个子类被使用了,并且这多个子类没有自己的+initialize方法,子类会根据OC消息发送流程而调用了它父类的+initialize方法。

或者可以这么理解,任何类在使用之前都会调用它的+initialize,如果这个类没有+initialize,那么就找它的父类的+initialize一直到NSObject类。并且不会自动调用 [super initialize]

Demo

为了验证上述的结果,做了如下的测试

调用0次

类:RXInitializeParentObject 所有DemoObject的父类

@implementation RXInitializeParentObject
+ (void)initialize {
    NSLog(@"Parent initialize");
}
- (void)print {
}
@end

测试类:RXInitializeTestObject

- (void)test_doNoting {
}

当然是什么也没有输出

调用多次

RXInitializeEmptyObject,子类没有自己的initialize

@implementation RXInitializeEmptyObject
@end

测试类:RXInitializeTestObject

- (void)test_empty {
    self.rxInitializeEmptyObject = [RXInitializeEmptyObject new];
    [self.rxInitializeEmptyObject print];
}

输出的结果:

Parent initialize
Parent initialize

第一行输出:是因为初始化RXInitializeEmptyObject会先初始化其父类RXInitializeParentObject
第二行输出:对于RXInitializeEmptyObject来说,根据OC的消息发送规则,它的initialize方法定位到了父类的initialize方法了

子类自定义的+initialize方法

RXInitializeCustomObject

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

测试类:RXInitializeTestObject

- (void)test_custom {
    self.rxInitializeCustomObject = [RXInitializeCustomObject new];
    [self.rxInitializeCustomObject print];
}

输出结果

Parent initialize
Custom initialize

第一行输出:是因为初始化RXInitializeCustomObject会先初始化其父类RXInitializeParentObject
第二行输出:对于RXInitializeCustomObject来说,它有自己的initialize方法。

先使用Empty再使用Custom

测试类:RXInitializeTestObject

- (void)test_empty_custom {
    self.rxInitializeEmptyObject = [RXInitializeEmptyObject new];
    [self.rxInitializeEmptyObject print];
    
    self.rxInitializeCustomObject = [RXInitializeCustomObject new];
    [self.rxInitializeCustomObject print];
}

输出结果

Parent initialize
Parent initialize
Custom initialize

第一行输出,是因为使用RXInitializeEmptyObject初始化其父类
第二行输出,是因为使用RXInitializeEmptyObject初始化自己
第三行输出,是因为使用RXInitializeCustomObject

先使用Custom再使用Empty

测试类:RXInitializeTestObject

- (void)test_custom_empty {
    self.rxInitializeCustomObject = [RXInitializeCustomObject new];
    [self.rxInitializeCustomObject print];
    
    self.rxInitializeEmptyObject = [RXInitializeEmptyObject new];
    [self.rxInitializeEmptyObject print];
}

输出结果

Parent initialize
Custom initialize
Parent initialize

大家可以自己尝试分析一下~~~

Custom & Custom2

RXInitializeCustom2Object,内容跟RXInitializeCustomObject几乎一样

@implementation RXInitializeCustom2Object
+ (void)initialize {
    NSLog(@"Custom 2 initialize");
}
@end

测试类:RXInitializeTestObject

- (void)test_custom_custom2
{
    self.rxInitializeCustomObject = [RXInitializeCustomObject new];
    [self.rxInitializeCustomObject print];
    
    self.rxInitializeCustom2Object = [RXInitializeCustom2Object new];
    [self.rxInitializeCustom2Object print];
}

输出结果:

Parent initialize
Custom initialize
Custom2 initialize

结果也是很容易分析的。

super Custom

RXInitializeSuperCustomObject

@implementation RXInitializeSuperCustomObject
+ (void)initialize
{
    [super initialize];
    NSLog(@"Super Custom initialize");
}
@end

测试类:RXInitializeTestObject

- (void)test_superCustom {
    self.rxInitializeSuperCustomObject = [RXInitializeSuperCustomObject new];
    [self.rxInitializeSuperCustomObject print];
}

输出结果

Parent initialize
Parent initialize
Super Custom initialize

其中第二行是因为[super initialize]导致的。

以上就是关于+initialize方法被调用次数探究,包括理论部分和Demo部分。

相关文章

  • initialize被调用次数探究

    概况 我们都知道+initialize方法会在此类第一次被使用的时候会被调用,那么调用的次数是靠什么来决定的了?结...

  • OC基础-category(3)

    initialize方法 initialize方法被调用的时机:initialize方法会在 “类” 在第一次 “...

  • iOS - +load 和 + initialize的区别

    1. +load 和+initialize调用时机次数 声明类Person 和Person+Category 不调...

  • 面试题总结

    01.+load和+initialize的区别是什么? 答:这个问题应该回答调用时刻,调用次数两个方面即可.+lo...

  • load、initialize详解及区别

    load、initialize方法的区别? load、initialize的调用顺序? load initialize

  • +initialize

    +initialize +initialize 方法的调用时机 +initialize 方法在 Class 第一次...

  • iSO底层原理 - initialize方法

    +initialize方法会在类第一次接收到消息时调用; 调用顺序: 先调用父类的+initialize,再调用子...

  • initialize方法解析

    +initialize方法会在类第一次接收到消息时调用; 调用顺序: 先调用父类的+initialize,再调用子...

  • +initialize

    ?+initialize会在类第一次接收到消息时调用?调用顺序先调用父类的+initialize再调用子类的+in...

  • iOS +load +initialize方法的一些浅谈

    load、initialize方法的区别 1.调用方式 load是根据函数地址直接调用 initialize是通过...

网友评论

      本文标题:initialize被调用次数探究

      本文链接:https://www.haomeiwen.com/subject/lveulqtx.html