美文网首页iOS底层
iOS-关联对象

iOS-关联对象

作者: xxxxxxxx_123 | 来源:发表于2020-02-26 20:33 被阅读0次

    initialize方法什么时候调用?

    首先我们创建一个TPerson类:

    @interface TPerson : NSObject
    @property (nonatomic, copy) NSString *name;
    
    @end
    
    #import "TPerson.h"
    
    @implementation TPerson
    
    + (void)initialize {
        NSLog(@"类-initialize");
    }
    
    @end
    

    然后我们在main函数中调用以下方法:

    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "TPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool{
            Class cls = [TPerson class];
            NSLog(@"---class----");
            TPerson *per = [TPerson alloc];
            NSLog(@"---alloc----");
        }
        return 0;
    }
    
    

    运行代码,我们在main函数打一个断点,可以看到在进入main函数之前控制台并没有输出,说明initialize是在运行时调用。跳过断点,控制台输出:

    2020-02-18 18:08:23.696091+0800 objc-debug[31356:847168] 类-initialize
    2020-02-18 18:08:23.696411+0800 objc-debug[31356:847168] ---class----
    2020-02-18 18:08:23.696592+0800 objc-debug[31356:847168] ---alloc----
    

    从结果,我们可以猜测,initialize的调用和class方法、或者alloc并没有直接关系。然而,当我们调用TPerson的某一个方法的时候,就会触发initialize的调用,联想到方法的调用本质就是发送消息,我们可以在lookUpImpOrForward查探一下究竟。

    IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        if (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
        }
    }
    

    根据代码我们可以判断,此处大概和initialize相关,设置一个断点,在调用[TPerson class]这个方法之后果然来了这里,跟踪代码:

    static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
    {
        return initializeAndMaybeRelock(cls, obj, lock, true);
    }
    
    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;
        }
    
        。。。。。。
        
        initializeNonMetaClass(nonmeta);
        return cls;
    }
    
    void initializeNonMetaClass(Class cls)
    {
        assert(!cls->isMetaClass());
    
        Class supercls;
        bool reallyInitialize = NO;
        
        .......
    
        // 确保在initialize类的时候,其父类已经在initializing了 
        supercls = cls->superclass;
        if (supercls  &&  !supercls->isInitialized()) {
            initializeNonMetaClass(supercls);
        }
        
        // 试着将类的初始化状态自动设置为 CLS_INITIALIZING.
        {
            if (!cls->isInitialized() && !cls->isInitializing()) {
                cls->setInitializing();
                reallyInitialize = YES;
            }
        }
        
        if (reallyInitialize) {
            callInitialize(cls);
        }
        ......
    }
    
    void setInitializing() {
        ISA()->setInfo(RW_INITIALIZING);
    }
        
    void callInitialize(Class cls)
    {
        ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    }
    
    

    顺着代码,我们发现,如果类没有initialize,就会以IMP的方式调用。

    然后我们保留class方法,注释掉alloc方法

    Class cls = [TPerson class];
    NSLog(@"---class----");
    // TPerson *per = [TPerson alloc];
    // NSLog(@"---alloc----");
    
    // 控制台输出
    2020-02-18 19:04:41.693528+0800 objc-debug[31838:896818] 类-initialize
    2020-02-18 19:04:41.693962+0800 objc-debug[31838:896818] ---class----
    

    然后我们注释class方法,保留alloc方法:

    // Class cls = [TPerson class];
    // NSLog(@"---class----");
    TPerson *per = [TPerson alloc];
    NSLog(@"---alloc----");
    
    // 控制台输出
    2020-02-18 19:05:06.043299+0800 objc-debug[31855:897517] 类-initialize
    2020-02-18 19:05:06.043415+0800 objc-debug[31855:897517] ---alloc----
    

    这也验证了我们的推测,initialize的调用确实和哪一个方法没有关系,只要是类第一次调用方法就会调用。

    分类的initialize方法

    那么分类如果也实现了initialize方法,什么时候调用呢?

    并且为其创建一个分类:

    #import "TPerson.h"
    
    @interface TPerson (addition)
    
    @end
    
    @implementation TPerson (addition)
    
    + (void)initialize {
        NSLog(@"分类-initialize");
    }
    
    @end
    

    运行程序,只看到控制台输出:

    2020-02-18 19:33:43.443330+0800 objc-debug[31951:908425] 分类-initialize
    

    这是因为分类方法都是通过attachList方法加入到类的ro或者rw中,而attachList方法是先将原来的列表进行扩容,然后将旧数据移动到列表的尾部位置,再把新的数据copy到列表头部位置。所以分类的方法在前面,主类方法在后面,方法查找的时候找到前面的方法就直接返回了,并不会再去调用主类的方法。

    总结

    initialize只会调用一次,在对类第一次发送消息的时候调用。
    主类和分类都有initialize方法,只会调用分类的方法。

    相关文章

      网友评论

        本文标题:iOS-关联对象

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