Objective-C
中其实有三种对象,分别为实例对象(instance)、类对象(class)、元类对象(meta-class);那这三种对象有什么关系呢?以及三种对象本身存储了哪些信息呢?这是我们本文要探讨的问题。
我们知道Objective-C
的类可以通过clang
编译器转换为C++
的结构体,也就是说,Objective-C
对象的本质就是结构体,下面通过实践来证明。
- 创建一个命令行项目(这里可以找到),在main.m文件添加如下的代码
#import <Foundation/Foundation.h>
@interface Person: NSObject {
int _age;
double _height;
}
@end
@implementation Person
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
然后使用clang
编译器重写main.m
文件,可指定平台iOS和架构arm64,在main.m所在的文件夹执行如下命令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
会得到一个main-arm64.cpp的文件,可看到Person
类转换为struct Person_IMPL
结构体
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
double _height;
};
存在上面定义Person
类时声明的_age
和_height
成员变量,发现第一个成员变量是struct NSObject_IMPL
结构体,这是NSObject
被clang
编译器重写后对应的结构体,在main-arm64.cpp
文件中可找到声明
struct NSObject_IMPL {
Class isa;
};
实例对象(instance)
Objictive-C
实例对象是通过类对象创建
Person *p1 = [Person new];
Person *p2 = [[Person alloc] init];
实例对象分配在堆空间,所以p1、p2指向的内存地址都在堆上;每次创建的都是不同的对象,所以在内存中会存在很多个实例对象。
把struct NSObject_IMPL
与struct Person_IMPL
合并一下得到下面的结构
struct Person_IMPL {
Class isa;
int _age;
double _height;
};
这也是Person
实例对象在内存中的布局情况。
总结一下实例对象中包含信息
- isa指针,指向类对象
- 成员变量
其实isa也是成员变量,只不过比较特殊,它指向这个实例对象的类对象。
类对象(class)
类对象是对实例对象的描述,描述这个类实例对象有哪些成员变量、属性、对象方法、类方法,那这些信息存储在什么地方呢?(下面提及的数据类型这里可以找到)
类对象声明为typedef struct objc_class *Class;
,再看看struct objc_class
结构体,截取一些关键的信息看看
struct objc_class : objc_object {
// 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
class_rw_t *data() {
return bits.data();
}
// ... 还有很多方法,略去
};
superclass
指向父类,其他的信息存储在class_rw_t
的结构体中,定义如下
struct class_rw_t {
const class_ro_t *ro; // 实例变量信息
method_array_t methods; // 对象方法信息
property_array_t properties; // 属性信息
protocol_array_t protocols; // 协议信息
// ... 省略其他信息
}
runtime
提供这些信息函数如下
获取实例变量
Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
获取属性信息
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
获取对象方法
// 第一个参数是 *类对象*,返回的是*对象方法*;如果是参数*元类对象*,返回的是*对象方法*
Method *
class_copyMethodList(Class cls, unsigned int *outCount)
获取协议信息
Protocol * __unsafe_unretained *
class_copyProtocolList(Class cls, unsigned int *outCount)
struct objc_class
继承objc_object
,struct objc_object
结构如下
struct objc_object {
private:
isa_t isa;
// ...省略方法
}
那isa_t
是联合体,结构如下
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
// ...省略
}
这里面有个 cls
成员,这个就是class
指向mata-class
的指针。
总结一下类对象(class)中存储的信息
- isa指针,指向元类的指针
- superclass指向父类的指针
- 成员变量(Ivar)的描述信息
- 属性(property)的描述信息
- 对象方法(Instance Method)的描述信息
- 遵循协议(protocol)的描述信息
元类对象(meta-class)
上面可以看出,meta-class也是一个Class 类型,与类对象的数据类型时一样的,只不过存储的信息不同。
元类对象(meta-class)存储的信息有
- isa指针,指向基类(NSObject)元类(meta-class)的指针,所有元类(meta-class)的isa指针都指向NSObject元类的指针。
- superclass指向父类的元类(meta-class)的指针
- 类方法(class Method)的描述信息
总结
上面讲了实例对象的内存布局,以及实例对象(instance)、类对象(class)、元类对象(meta-class)之间的关系,最后用一张图来说明它们之间的关系。
class-inherit.png
网友评论