美文网首页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-关联对象

    以UIAlertView为例,在创建时,对事件进行处理。 objc_setAssociatedObject 此方法...

  • iOS-关联对象

    initialize方法什么时候调用? 首先我们创建一个TPerson类: 然后我们在main函数中调用以下方法:...

  • ios-关联对象

    关联对象常见的一种做法 在分类里想增加属性,由于只会生成 set get方法,并不会存在 属性与成员 这个时候的做...

  • iOS-底层-关联对象

    前两篇文章我们学习了关于Category的知识Category分类和load和initialize,现在再看一个问...

  • iOS-关联对象技术

    为某个对象设置关联对象的值: 根据给定的键从某对象中获取相应的关联对象值: 移除指定对象的关联对象: 注:id v...

  • iOS-底层原理(7)-关联对象

    面试题 1. Category能否添加成员变量?如果可以,如何给Category添加成员变量? 不能直接给Cate...

  • iOS-浅谈OC中的关联对象

    目录 前言工作中遇到问题的思考---- 直接在分类添加成员变量---- 在分类中增加属性如何在分类中添加成员变量?...

  • Swift 为分类增加属性objc_getAssociated

    OC 获取关联对象 Swift 获取关联对象——错误的写法 Swift 获取关联对象——正确的写法 设置关联对象 ...

  • iOS runtime关联对象 objc_setAssociat

    关联对象的作用: 关联对象可以给某个对象关联一个或者多个其他对象,这些对象通过健来区分。 创建存储关联对象objc...

  • 关联对象

    关联对象的方式 关联对象源码基本思路 关联对象的结构:AssociationsHashManager // Ass...

网友评论

    本文标题:iOS-关联对象

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