前言
通过clang将objective c代码翻译成cpp代码,阅读代码的实现去理解objc中对象模型。
objective c代码
#import <Foundation/Foundation.h>
@interface TestA : NSObject {
int age;
NSString *name;
}
- (void)instanceMethod;
+ (void)classMethod;
@end
@implementation TestA
- (void)instanceMethod {
NSLog(@"instance method~");
}
+ (void)classMethod {
NSLog(@"class method~");
}
@end
代码块中含有类方法以及普通的方法。
clang翻译objective c代码块
clang -rewrite-objc TestA.m
Objective c的对象模型
先介绍一下objective c runtime下id以及class类型。
struct objc_class {
struct objc_class *isa;
.....
};
struct objc_object {
struct objc_class *isa;
};
typedef struct objc_class *Class; //类 (class object)
typedef struct objc_object *id; //对象 (instance of class)
struct objc_object * id代表一个class instance的对象,在objc_object结构体中有一个成员isa,这个变量指向id对象所属class。
struct objc_class *Class代表一个类对象,在objc_class结构体中有一个成员isa,这个变量指向类对象的meta-class。
注意这两个结构体中的isa指向的内容是不同的。
通过clang的命令,已经将TestA.m文件翻译成TestA.cpp代码。
struct _class_t OBJC_CLASS_$_TestA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_TestA,
0, // &OBJC_CLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_TestA,
};
static struct _class_ro_t _OBJC_CLASS_RO_$_TestA __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, __OFFSETOFIVAR__(struct TestA, age), sizeof(struct TestA_IMPL),
(unsigned int)0,
0,
"TestA",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_TestA,
0,
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_TestA,
0,
0,
};
这里 struct class_t OBJC_CLASS$_TestA 对应的就是前面介绍的 struct objc_class 结构体,我们可以观察struct objc_class 结构体中包含的信息。这里我们转译一下此结构体方便阅读。
struct objc_class {
Class isa;
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
} ;
对比上下两个结构体,可以看出整个struct objc_class所包含的信息。
这里主要介绍几个重要的信息。
变量 | 含义 |
---|---|
isa | class的meta-class, TestA metaclass是OBJC_METACLASS_$_TestA |
super_class | class的superclass, TestA superclass是OBJC_CLASS_$_NSObject |
const char *name | 类名, 在结构体中OBJC_CLASS_RO$_TestA中赋值为”TestA” |
struct objc_ivar_list *ivars | 成员变量列表 |
struct objc_method_list **methodLists | 普通函数表(即非类函数) |
struct objc_cache *cache | 最近使用函数列表,objc_msgsend过程中查找函数地址时使用缓存表 |
初始化
static void OBJC_CLASS_SETUP_$_TestA(void ) {
OBJC_METACLASS_$_TestA.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_TestA.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_TestA.cache = &_objc_empty_cache;
OBJC_CLASS_$_TestA.isa = &OBJC_METACLASS_$_TestA;
OBJC_CLASS_$_TestA.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_TestA.cache = &_objc_empty_cache;
}
结合静态函数OBJC_CLASS_SETUP_$$_TestA(void)
可以看到OBJC_METACLASS_$_TestA.isa被赋值给了NSObject的meta-class, OBJC_METACLASS_$_TestA.superclass也被赋值给了OBJC_METACLASS_$_NSObject
函数调用的区别
调用方法一:
TestA * instance = [[TestA alloc] init];
[instance instanceMethod];
通过上述讲解,整个过程首先会找到TestA * instance对应的struct objc_object结构体,通过struct objc_object中的isa指针,找到TestA的类对象,然后在TestA类对象的结构体struct objc_class中的method list查找instanceMethod函数。
调用方法二:
[TestA classMethod];
类函数的调用过程:这里会直接找到TestA类对象的结构体struct objc_class中的isa指针,而struct objc_class中的isa指向的是TestA的meta-class即OBJC_METACLASS_$_TestA,再通过查找meta-class的存储的类函数,实现函数的调用。
总结:通过上述讲解,大家可以看出普通的函数地址存储在struct objc_class结构体中methodLists中,而类方法的地址则存储在类对象对应的meta-class中。
让我们继续看meta-class结构体。
meta-class结构体
meta-class结构体的定义
extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_TestA __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_NSObject,
0, // &OBJC_METACLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_METACLASS_RO_$_TestA,
};
meta-class结构体的定义和struct objc_class完全相同。
整体结构
希望通过本文,可以加深你对objective c的对象模型的理解,如有错误,请提出,我会及时改正,大家共同学习。