iOS 底层原理 | OC 对象的本质
一、Objective-C 的本质
Objective-C 代码的底层都是通过 C/C++ 实现,所以 Objective-C 面向对象是基于 C/C++ 数据结构实现。 下图为OC语言转换成机器语言的几个过程。
二、Objective-C 对象本质
通过终端指令:(注:如果需要链接其他框架,使用 -framework 参数。)
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main_arm64.cpp
将 OC 代码转为 C++ 代码,得 NSObject
底层结构:
struct NSObject_IMPL {
Class isa;
};
Class 是一个指向对象的结构体指针
typedef struct objc_class *Class;
所以 NSObject
是一个结构体,初始内部只有一个指向对象的结构体指针 isa。 由于 iOS 为64位系统,所以一个指针占 8bytes( 1byte = 8bit ),但是实际上系统给它分配了 16bytes 的内存空间。
NSLog(@"%zd",class_getInstanceSize([NSObject class])); //实例对象的成员所占用的大小 8bytes(实际使用的)
NSLog(@"%zd",malloc_size((__bridge const void *)(obj))); //整个结构体占用的是 16bytes(实际分配的)
OC 源码中所有对象至少分配 16 bytes
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
思考
一个 Person 对象、一个 Student 对象占用多少内存空间?
Person 和 Student 对象实际占用内存 16bytes,系统分配内存也是 16bytes。(注:这里实际占用为 16bytes 是因为结构体内存对齐)
三、Objective-C 对象分类
- Instance 对象(实例对象)
- Class 对象(类对象)
- Meta-Class 对象(元类对象)
1. Instance 对象(实例对象)
Instance 对象在内存中存储的信息包括
- isa 指针
- 其他成员变量
2. Class 对象(类对象)
每个类在内存中有且仅有一个 Class 对象。
Class 对象在内存中存储的信息包括:
3. Meta-Class 对象(元类对象)
每个类在内存中也是有且仅有一个 Meta-Class 对象。
objectMetaClass
是 NSObject
的 meta-class 对象(元类对象)。
Class objectMetaClass = object_getClass([NSObject class]); // Runtime API
// 注:以下获取的是 objectClass 是 Class 对象,并不是 Meta-Class 对象
Class objectClass = [[NSObject class] class];
Meta-Class 对象在内存中存储的信息包括:
四、isa 指针 和 superclass 指针
1. isa 指针
- Instance 的 isa 指向 Class。当调用对象方法的时,通过 Instance 的 isa指针找到 Class,最后找到 对象方法 的实现进行调用。
- Class 的 isa 指向 Meta-Class。当调用对象方法的时,通过 Class 的 isa指针找到 Meta-Class,最后找到 类方法 的实现进行调用。
从 64bit 开始,isa 需要进行一次位运算,才能计算出真实地址,OC 源码中定义的 ISA_MASK。
# if __arm64__
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# endif
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# else
# error unknown architecture for packed isa
# endif
Class、Meta-Class 对象的本质结构都是 struct objc_class
。
2. Class 对象的 superclass 指针
当 Instance 对象要调用父类的 对象方法 时,会先通过 isa 找到 Instance 对象的 Class,然后通过 superclass 找到 Instance 对象父类的 class,最后找到 对象方法 的实现进行调用。
3. Meta-Class 对象的 superclass 指针
当Instance 对象的 Class 要调用 Instance 父类对象的 类方法 时,会先通过 isa 找到 Instance 对象的 Meta-Class,然后通过 superclass 找到Instance 父类对象的 Meta-Class,最后找到 类方法 的实现进行调用。
4. 总结 isa 指针和 superclass 指针
- Instance 调用对象方法的轨迹:isa 找到 Class,方法不存在,就通过superclass 找父类。
- Class 调用类方法的轨迹:isa 找 Meta-Class,方法不存在,就通过 superclass 找父类
5. 窥探 struct objc_class 的结构
objc4源码下载
#include <runtime.h>
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
#include <objc/objc.h>
typedef struct objc_class *Class;
/// 实例类
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
#include <objc-runtime-new.h>
// Class 类
struct objc_class : objc_object {
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits; // 用于获取具体的类信息
...
};
bits
& FAST_DATA_MASK
得到 class_rw_t
数据地址。得
#include <objc-runtime-new.h>
struct class_rw_t {
uint32_t flags;
Class firstSubclass;
Class nextSiblingClass;
explicit_atomic<uintptr_t> ro_or_rw_ext;
...
public:
...
const class_ro_t *ro() const {...}
const method_array_t methods() const {...} // 方法列表
const property_array_t properties() const {...} // 属性列表
const protocol_array_t protocols() const {...} // 协议列表
}
class_ro_t *ro
内部结构
#include <objc-runtime-new.h>
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance 对象占用的内存空间
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name; // 类名
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
...
}