美文网首页
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被调用次数探究

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