iOS底层

iOS-关联对象

2020-02-26  本文已影响0人  xxxxxxxx_123

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方法,只会调用分类的方法。

上一篇下一篇

猜你喜欢

热点阅读