OC的一门面向对象的开发语言,是由C,C++,汇编混编而成,那么对象的本质是什么,在底层的结构是什么样的,值得我们探索。
对象的本质
在main.m中创建一个SwwPerson
对象,通过xcrun
命令编译成c++
。
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main_arm64.cpp
复制代码
打开main_arm64.cpp文件,找到SwwPerson,可以看出对象的本质就是结构体,对象的继承就是父类作为成员变量?,所有对象的结构体的类型都是objc_object
typedef struct objc_object SwwPerson;
struct SwwPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_SwwName;
};
复制代码
objc_object
是只包含一个成员变量isa
的结构体
struct objc_object {
Class _Nonnull isa __attribute__((deprecated));
};
复制代码
我们常用的id
类型,是本质是一个结构体指针
typedef struct objc_object *id;
复制代码
类Class
的结构体类型是objc_class
,也是一个结构体指针
typedef struct objc_class *Class;
复制代码
isa
位域
在程序中,某些信息存储时不需要一个完整的字节,只需要几位,为节省存储空间C语言支持“位域”的结构体。下面定义了一个结构体和一个位域,结构体的大小为12字节,位域的大小为4字节(最宽基本类型成员大小的整数倍),位域内部的存储有自己的规则,有兴趣的同学可以自行百度。
//结构体
struct str1
{
int a;
int b;
int c;
};
//位域
struct str2
{
int a:1;
int b:2;
int c:6;
};
复制代码
联合体(共用体)
联合体union和结构体struct在类型定义、变量定义、使用方法上很相似。但是联合体内部成员是互斥的,他们共用一块内存地址。union的sizeof测到的大小实际是union中各个元素里面占内存最大的那个元素的大小。
union uni
{
int a;
int b;
int c;
}
复制代码
isa
isa的结构是isa_t,是一个联合体位域,如下为isa_t的简写,bits是一个位域。
union isa_t {
uintptr_t bits;
Class cls;
}
复制代码
arm64架构下的bits
uintptr_t nonpointer : 1; 是否对 isa 指针开启指针优化 0纯isa指针 1包含其他信息
uintptr_t has_assoc : 1; 关联对象标志位,0没有,1存在
uintptr_t has_cxx_dtor : 1; 是否有 C++ 或者 Objc 的析构器,如果有析构函数,则需要做析构逻辑, 如果没有,则可以更快的释放对象
uintptr_t shiftcls : 33; 存储类指针的值
uintptr_t magic : 6; 用于调试器判断当前对象是真的对象还是没有初始化的空间
uintptr_t weakly_referenced : 1; 对象是否被指向或者曾经指向一个 ARC 的弱变量,没有弱引用的对象可以更快释放。
uintptr_t unused : 1; 暂无使用
uintptr_t has_sidetable_rc : 1; 是否使用sidetable存储引用计数
uintptr_t extra_rc : 19 引用计数
复制代码
在objc4的可调试源码中,使用isa拿出类的指针。
- 位移运算(x86架构,shiftclass占44位)
首先拿到person
的isa
,因为使用的源码调试,所以是x86架构(非M1),shiftclass占44位,又边3位,左边17位。想要只留下shiftclass则需要将isa
,先右移3位,再左移20位,再右移17。
- ISA_MASK 面具
拿到person
的isa
,和ISA_MASK做与运算,得到shiftclass