Objective-C的本质
-
我们平时编写的Objective-C代码,底层实现其实都是C\C++代码
- 所以Objective-C的面向对象都是基于C\C++的数据结构实现的
- 将Objective-C代码转换为C\C++代码
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件
如果需要链接其他框架,使用-framework参数。比如-framework UIKit
Objective-C对象在内存中的布局
- 新建一个Person对象,通过观察转换的cpp文件,找到以下结构体定义
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _age;
int _height;
};
- 在Person_IMPL中包含一个结构体成员NSObject_IVARS,它是NSObject_IMPL类型.
struct NSObject_IMPL {
Class isa;
};
- 由此可以看出,OC中的对象其实就是通过结构体来实现的。在NSObject_IMPL包含了一个Class类型的成员isa。继续查看Class的定义:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
- 可以发现其实Class就是一个objc_class类型的结构体指针。在最新的objc4的源码中的objc-runtime-new.h文件中,可以找到最新的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
......
}
- objc_class继承自结构体objc_object,而结构体objc_object的具体定义如下,内部只有一个isa指针
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
- 由于继承关系,结构体objc_class自然也就继承了objc_object的isa指针,所以objc_class也可以转换成如下写法:
struct objc_class {
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
......
}
- isa是继承自objc_object的属性
- superclass表示当前类的父类
- cache则代表方法缓存。
- bits是class_data_bits_t类型的属性,用来存放类的具体信息。
- 查看class_data_bits_t的具体实现如下:
//此处只列出核心的代码
struct class_data_bits_t {
......
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
......
}
- 这时候发现了通过bits的内部函数data()可以拿到class_rw_t类型的数据,查看class_rw_t的源码如下:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
explicit_atomic ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
private:
using ro_or_rw_ext_t = objc::PointerUnion;
...
public:
const class_ro_t *ro() const { //获取class_ro_t类信息
auto v = get_ro_or_rwe();
if (slowpath(v.is())) {
return v.get()->ro;
}
return v.get();
}
const method_array_t methods() const { //方法列表
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get()->methods;
} else {
return method_array_t{v.get()->baseMethods()};
}
}
const property_array_t properties() const { //属性列表
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get()->properties;
} else {
return property_array_t{v.get()->baseProperties};
}
}
const protocol_array_t protocols() const { //协议列表
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get()->protocols;
} else {
return protocol_array_t{v.get()->baseProtocols};
}
}
...
- class_rw_ext_t的结构:
const class_ro_t *ro;
method_array_t methods;
property_array_t properties;
protocol_array_t protocols;
char *demangledName;
uint32_t version;
};
- 方法列表methods
- 属性列表properties
- 协议列表protocols。
- 一个class_ro_t类型的只读变量ro
继续查看class_ro_t的结构如下:
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; //当前instance对象占用内存的大小
const uint8_t * ivarLayout;
const char * name; //类名
method_list_t * baseMethodList; //基础的方法列表
protocol_list_t * baseProtocols;//基础协议列表
const ivar_list_t * ivars; //成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;//基本属性列表
}
此处就不得不说class_rw_t和class_ro_t的区别了,class_ro_t中存放着类最原始的方法列表,属性列表等等,这些在编译期就已经生成了,而且它是只读的,在运行期无法修改。而class_rw_t不仅包含了编译器生成的方法列表、属性列表,还包含了运行时动态生成的方法和属性。它是可读可写的。
通过lldb打印出oc对象的结构
通过类信息直接打印
- 可以通过lldb调试打印出objc_class结构体信息
p (objc_class *)$0
(objc_class *) $5 = 0x00000001000025b0
(lldb) p *$5
(objc_class) $6 = {
objc_object = {
isa = {
cls = 0x0000000100002588
bits = 4294976904
= {
nonpointer = 0
has_assoc = 0
has_cxx_dtor = 0
shiftcls = 536872113
magic = 0
weakly_referenced = 0
deallocating = 0
has_sidetable_rc = 0
extra_rc = 0
}
}
}
superclass = NSObject
cache = {
_buckets = {
std::__1::atomic = 0x00000001003ea460 {
_sel = {
std::__1::atomic = 0x0000000000000000
}
_imp = {
std::__1::atomic = 0
}
}
}
_mask = {
std::__1::atomic = 0
}
_flags = 32804
_occupied = 0
}
bits = (bits = 4320180788)
}
(lldb) p $6.bits
(class_data_bits_t) $7 = (bits = 4320180788)
(lldb) p $6.data()
(class_rw_t *) $8 = 0x000000010180ba30
(lldb) p *$8
(class_rw_t) $9 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic = 4294976120
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
- 通过地址偏移获取class_data_bits_t的信息,首先计算class_data_bits_t的偏移值为32(isa-8+superclass-8+cache-16)
- isa和superclass占8个字节很好理解,我们来分下下cache为何会有16个字节?cache_t的结构如下去掉一些不占字节的(static修饰的和方法)
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED//电脑或模拟器
explicit_atomic _buckets;//explicit_atomic相当于做了一个线程保护,实际类型为一个结构体指针占8个字节
explicit_atomic _mask;//typedef uint32_t mask_t; 占4个字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16//(__arm64__) && __LP64__真机设备
explicit_atomic _maskAndBuckets;// typedef unsigned long uintptr_t; 8个字节
mask_t _mask_unused;//和上面一样4个字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4//(__arm64__) && !__LP64__低于64位的真机设备
// _maskAndBuckets stores the mask shift in the low 4 bits, and
// the buckets pointer in the remainder of the value. The mask
// shift is the value where (0xffff >> shift) produces the correct
// mask. This is equal to 16 - log2(cache_size).
explicit_atomic _maskAndBuckets;
mask_t _mask_unused;
#else
#error Unknown cache mask storage type.
#endif
#if __LP64__
uint16_t _flags; //typedef unsigned short uint16_t; 2个字节
#endif
uint16_t _occupied; //2个字节
};
//所以无论在什么设备上cache_t都是16个字节
- 回到刚刚的lldb调试
(lldb) p/x Person.class
(Class) $0 = 0x00000001000025b0 Person
(lldb) p/x (class_data_bits_t *)(0x00000001000025b0 + 0x20)
(class_data_bits_t *) $1 = 0x00000001000025d0
(lldb) p *$1
(class_data_bits_t) $2 = (bits = 4320180788)
(lldb) p/x $2.data()
(class_rw_t *) $3 = 0x000000010180ba30
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2148007936
witness = 0
ro_or_rw_ext = {
std::__1::atomic = 4294976120
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
- 总结,上面两种方法都可以获取到对象的信息剩下的操作就一样的了,不过这种lldb调试需要在搭配好的源码环境下才能正常运行。
通过定义结构体的方法查看对象的结构
- 根据源码的的对象结构,定义下面对应的结构体
struct xq_bucket_t {
//#if __arm64__
// explicit_atomic _imp;
// explicit_atomic _sel;
//#else
IMP _imp;
SEL _sel;
//#endif
};
struct xq_cache_t {
//#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
// struct xq_bucket_t * _buckets;
// mask_t _mask;
//#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
uintptr_t _maskAndBuckets;
mask_t _mask_unused;
//#endif
//#if __LP64__
uint16_t _flags;
//#endif
uint16_t _occupied;
};
struct xq_class_data_bits_t {
// Values are the FAST_ flags above.
uintptr_t bits;
};
struct xq_objc_class {
Class ISA;
Class superclass;
struct xq_cache_t cache; // formerly cache pointer and vtable
struct xq_class_data_bits_t bits;
};
Person *p = [Person alloc];
struct xq_objc_class *pClass = (__bridge struct xq_objc_class *)([Person class]);
// 通过强转也可以达到调试效果