iOS-关联对象
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
方法,只会调用分类的方法。