一、源码静态解读
1、在objc最新版本objc-781,可以在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
class_rw_t *data() const {
return bits.data();
}
//下面都是方法代码省略......
}
在这段源码解读中可得
-
objc_class
继承自objc_object
,所以拥有属性有isa
;还有其中自带的属性superclass;cache;bits
- 我们知道
sizeof(isa)=8
sizeof(superclass)=8
但是cache
是多少,需要我们接下来的分析; - 只要我们能算出前面三个属性的大小
isa
,superclass
,cache
,就能通过objc_class 首地址偏移
得到指向bits
的指针。
分析cache类cache_t
通过源码
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic _buckets;
explicit_atomic _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic _maskAndBuckets;
mask_t _mask_unused;
// How much the mask is shifted by.
static constexpr uintptr_t maskShift = 48;
// Additional bits after the mask which must be zero. msgSend
// takes advantage of these additional bits to construct the value
// `mask << 4` from `_maskAndBuckets` in a single instruction.
static constexpr uintptr_t maskZeroBits = 4;
// The largest mask value we can store.
static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
// The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.
static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
// Ensure we have enough bits for the buckets pointer.
static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers.");
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
// _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;
static constexpr uintptr_t maskBits = 4;
static constexpr uintptr_t maskMask = (1 << maskBits) - 1;
static constexpr uintptr_t bucketsMask = ~maskMask;
#else
#error Unknown cache mask storage type.
#endif
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
//.......省略很多代码
}
我们了解到在编译过程中分三种情况分别是宏定义
1、
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
;
2、#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
;
3、#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
不过无论哪种情况都会定义一个指针类型
(struct bucket_t * 或 uintptr_t) 和mask_t
类型;其大小分别为8字节
和4字节
Note:
1、explicit_atomic
源码为struct explicit_atomic : public std::atomic
是一个{...} std::atomic
的继承,对定义的模板类型
提供原子的操作,其内存分布大小就是模板类型
大小;在这里就是指针类型的8字节
2、mask_t 的定义可以看到在64位系统中typedef uint32_t mask_t;
所以就是uint32_t
大小4字节
最后我们可以看到还有_flags
和_occupied
,不用我说大家也都知道都是4字节
;
所以:cache_t
结构体所需内存大小为8+4+2+2 = 16;
(属性顺序配的很好,结构体内存对齐刚好排满)
objc_class
三个属性大小已经出来分别是sizeof(isa)=8
sizeof(superclass)=8
sizeof(cache)=16
所以其三者的和为32
用16进制表示即0x20
二、动态调试分析
1、在可运行的源码项目中准备代码
#import
@interface JJPerson : NSObject
@property(nonatomic, strong) NSString* name;
@end
@implementation JJPerson
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
JJPerson *person = [[JJPerson alloc] init];
person.name = @"KC is the best";
NSLog(@" person 实例指针:%p", person);
NSLog(@"JJPerson类型指针:%p", JJPerson.class);
}
return 0;
}
在其中任意处添加断点都行.
我在其中16进制打印了JJPerson
的类对象首地址
即图中的0x0000000100002388
在前面的分析的到,JJPerson
类对象首地址偏移0x20
的地址即bits
地址;在lldb
调试中
(lldb) p (class_data_bits_t*)(0x0000000100002388 + 0x20)
(class_data_bits_t *) $1 = 0x00000001000023a8
(lldb)
在objc_objc源码中
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() const {
return bits.data();
}
//下面都是方法代码省略......
}
了解到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;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
private:
using ro_or_rw_ext_t = objc::PointerUnion;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe}.storeAt(ro_or_rw_ext, memory_order_release);
}
class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
public:
void setFlags(uint32_t set)
{
__c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
}
void clearFlags(uint32_t clear)
{
__c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
ASSERT((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast();
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is())) {
return v.get();
} else {
return extAlloc(v.get());
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is())) {
return v.get()->ro;
}
return v.get();
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is()) {
v.get()->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
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};
}
}
};
其中我们发现了在结构体最后几个重要的方法 methods()
properties()
protocols()
,通过名字的猜测相信大家已经知道分别是方法
、属性
、协议
注意这里返回的手势一个列表list
哦!
我们现在不妨看一下这个properties
是个啥吧
这里面居然是一个list我们不妨取出来,看看里面到底是个啥,都有啥,下面是我lldb的调试过程
(lldb) p $2.list
(property_list_t *const) $3 = 0x0000000100002138
(lldb) p $3->count #查看list大小
(uint32_t) $4 = 1
(lldb) p $3->get(0) #查看第一个元素
(property_t) $5 = (name = "name", attributes = "T@\"NSString\",&,N,V_name")
可以看到这里面居然存有一个元素
,名称为name
这不就是我定义的类型吗?
@interface JJPerson : NSObject
@property(nonatomic, strong) NSString* name;
@end
我们用同样的方法可以获取到methods
信息如下
(lldb) p $1->data()->methods().list
(method_list_t *const) $11 = 0x00000001000020c0
(lldb) p $11->count
(uint32_t) $12 = 3
(lldb) p $11->get(0)
(method_t) $13 = {
name = ".cxx_destruct"
types = 0x0000000100000f42 "v16@0:8"
imp = 0x0000000100000bf0 (KCObjc`-[JJPerson .cxx_destruct] at main.m:14)
}
(lldb) p $11->get(1)
(method_t) $14 = {
name = "name"
types = 0x0000000100000f2f "@16@0:8"
imp = 0x0000000100000ba0 (KCObjc`-[JJPerson name] at main.m:11)
}
(lldb) p $11->get(2)
(method_t) $15 = {
name = "setName:"
types = 0x0000000100000f37 "v24@0:8@16"
imp = 0x0000000100000bc0 (KCObjc`-[JJPerson setName:] at main.m:11)
}
(lldb)
发现这里居然存有3个方法、分别是[JJPerson .cxx_destruct]
,[JJPerson name]
,[JJPerson setName:]
好神奇啊!居然JJPerson
实例的方法存储在JJPerson
类对象objc_class
中.
三 、总结
1、OC语言中我们实例化出来的对象(objc_object或者子类
),它的方法存储在对应的类对象(objc_class
)中.
2、类对象
有实例化出来的属性的信息,但不是其具体的值(比如需要名字属性);