案例分析
1、通过终端cd 到main.m文件所在目录
2、终端输入
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main64.cpp
编译会得到一个c++文件(具体问什么这么做呢,可参考iOS底层-对象的本质查看编译后的文件,我们可以得出实例对象实质为结构体,该结构体包含类指针和成员变量。
从图中可以看出
NSObject
也是objc_object
类型的结构体,其内部一样包含isa
指针和其他成员变量。在内存中isa地址就是instance的地址,其他成员变量依次排在后面。打印结果如下
从打印结果中可以发现class1和class2的地址一样,class3、class4和class5的地址一样。
通过苹果官网下载objc源码,从源码中我们可以找到
Class
的源码实现。我们可以得出结论
Class
是objc_class
的结构体指针。那么objc_class
和objc_object
的内部是怎么实现的呢?
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
...代码太长省略
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA(bool authenticated = false);
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
uintptr_t isaBits() const;
...代码太长省略
从源码从可以看到类中class_rw_t
存储了变量、方法列表、协议列表、版本等信息。
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
private:
// 部分代码已省略
// 方法列表
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
// 属性列表
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
//协议列表
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
};
下面我们通过案例结合lldb
分析类、元类和根元类
我们可以得到类对象
isa
指向的类对象我们称之为`元类。实例方法存储在类对象中,类方法存储在元类对象中。对象、类、元类我们一般通过下面的关系图说明由图可见元类的isa
指向基类的元类。基类的元类的 superclass
指向基类
总结
- 类的本质是对象
- 类对象是一种数据结构,类存储的基本信息包含类大小、名称、版本、继承层次和消息与函数的映射表
- 对象方法存放在类里面
- 类方法存放在元类里面
- 成员变量存放在
class_ro_t
中的ivar_list_t
- 属性在
class_rw_t
中的property_array_t
和class_ro_t
中的的property_list_t
都存着一份,并且会生成实例变量,和对应的方法 - 方法在
class_rw_t
中的method_array_t
和class_ro_t中
的的method_list_t
都存着一份 -
instance
对象的isa
指向Class
对象 -
Class
对象的isa
指向meta-class
对象 -
meta-class
对象的isa
指向基类的meta-class
对象