ISA走位
定义一个继承NSObject的类ABPerson,通过lldb命令查看其ISA的指向。
实例对象的ISA指向类对象
p/x p打印实例对象p的地址x/4gx 0x0000000100538ae0打印该地址的内存,输出4段,首段存储着ISA相关信息p/x 0x011d800100008185 & 0x00007ffffffffff8ULL,通过约ISA_MASK(0x00007ffffffffff8ULL)的&操作,得到类对象ABPersonpo 0x0000000100008180打印类对象 结论:实例对象的ISA指向类对象
类对象的ISA指向其元类
由上图可知0x0000000100008180是类对象的地址,继续查看类对象的ISA的指向:
能够看到得到了一个地址
0x0000000100008158,打印也是ABPerson,这是就是元类,是由系统生成。类对象只有一个,可证明如下:
类对象
ABPerson的地址都是:0x100008190。0x0000000100008158是元类地址。
结论:类对象的ISA指向其元类
元类的ISA指向根元类
由上图可知0x0000000100008158是类的元类地址,继续查看元类的ISA的指向:
能够看到元类的
ISA是指向NSObject,也就是根元类。
结论:元类的ISA是指向根元类
根元类的ISA指向其自身
由上图可知0x00007fff88967fe0是根元类NSObject的地址,继续查看根元类的ISA的指向:
红框的地址都是相同的,都是其自身。
结论:根元类的ISA指向其自身
ISA走位图
继承链
定义一个子类ABTeacher继承ABPerson
NSObject的元类、根元类、根根元类都是0x7fff88967fe0,是一个东西。ABPerson是ABTeacher是父类,ABTeacher元类的父类地址是0x100008210,ABPerson的元类地址也是0x100008210,所以ABTeacher元类的父类就ABPerson的元类。- 根类
NSObject的父类为nil - 根元类的父类地址是
0x7fff88968008就是NSObject
最后附上苹果官方的的图
类的结构
objc源码中查看Class的定义:
typedef struct objc_class *Class;
objc_class的结构体定义:
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// 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和superclass都占用一个结构体指针的大小8字节cache占16个字节,cache_t结构体:
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; // 8
union {
//联合体互斥所以占8
struct {
explicit_atomic<mask_t> _maybeMask; //uint32_t 4
#if __LP64__
uint16_t _flags; //uint16_t 2
#endif
uint16_t _occupied; //uint16_t 2
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache; // 8
};
_bucketsAndMaybeMask占8,当前union占8,所以cache_t占16
如果想访问bits就需要内存平移8+8+16=32个字节。
获取类的属性
lldb调试:
调试工程下载
@interface LGPerson : NSObject
// isa
@property (nonatomic, copy) NSString *name;
@property (nonatomic) int age;
- (void)saySomething;
@end
x/4gx LGPerson.class获取类的首地址p/x 0x1000083a0 + 0x20,首地址偏移32个字节,拿到bitsp (class_data_bits_t *)0x00000001000083c0,将地址强转成class_data_bits_t类型p $2->data(),调用class_data_bits_t中的data()函数
struct class_data_bits_t {
省略部分代码
public:
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
省略部分代码
}
p $3->properties()获取类的属性
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
class property_array_t :
public list_array_tt<property_t, property_list_t, RawPtr>
{
typedef list_array_tt<property_t, property_list_t, RawPtr> Super;
public:
property_array_t() : Super() { }
property_array_t(property_list_t *l) : Super(l) { }
};
class list_array_tt {
struct array_t {
uint32_t count;
Ptr<List> lists[0];
static size_t byteSize(uint32_t count) {
return sizeof(array_t) + count*sizeof(lists[0]);
}
size_t byteSize() {
return byteSize(count);
}
};
省略部分代码
}
property_array_t继承自list_array_tt,list_array_tt中有个结构体array_t,结构体中有变量lists
p $4.list拿到property_list_t首地址p $5.ptr打印property_list_t首地址p *$6取地址拿到property_list_tp $7.get(0)读取property_list_t中的成员变量
获取类的实例方法
- 前面相同的步骤这里直接省略介绍
p $3->methods()获取类的方法
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
class method_array_t :
public list_array_tt<method_t, method_list_t, method_list_t_authed_ptr>
{
typedef list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> Super;
public:
method_array_t() : Super() { }
method_array_t(method_list_t *l) : Super(l) { }
const method_list_t_authed_ptr<method_list_t> *beginCategoryMethodLists() const {
return beginLists();
}
const method_list_t_authed_ptr<method_list_t> *endCategoryMethodLists(Class cls) const;
};
class list_array_tt {
struct array_t {
uint32_t count;
Ptr<List> lists[0];
static size_t byteSize(uint32_t count) {
return sizeof(array_t) + count*sizeof(lists[0]);
}
size_t byteSize() {
return byteSize(count);
}
};
省略部分代码
}
method_array_t继承自list_array_tt,list_array_tt中有个结构体array_t,结构体中有变量lists
p $4.list拿到method_list_t首地址p $5.ptr打印method_list_t首地址p *$6取地址拿到method_list_tp $7.get(0).big()读取method_list_t中的实例方法
struct property_t {
const char *name;
const char *attributes;
};
struct method_t {
static const uint32_t smallMethodListFlag = 0x80000000;
method_t(const method_t &other) = delete;
// The representation of a "big" method. This is the traditional
// representation of three pointers storing the selector, types
// and implementation.
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
省略部分代码
}
对比结构体property_t和method_t能够看到,获取属性可直接$7.get(0)拿到结构体,而method_t里面还有一个结构体big,具体方法是放在big里面,所以要调用big()。
获取类的成员变量
修改LGPerson代码,添加一个成员变量,一个类方法
@interface LGPerson : NSObject
{
//成员变量
NSString *subject;
}
@property (nonatomic, copy) NSString *name;
@property (nonatomic) int age;
- (void)saySomething;
//类方法
+ (void)doSomething;
@end
@implementation LGPerson
- (void)saySomething{
NSLog(@"%s",__func__);
}
+ (void)doSomething{
NSLog(@"%s",__func__);
}
@end
- 前面相同的步骤这里直接省略介绍
-
$3->ro(),获取class_ro_t地址
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
p *$4取地址,获得class_ro_tp $5.ivars获取class_ro_t的成员变量列表ivar_list_t的首地址
struct class_ro_t {
省略部分代码
const ivar_list_t * ivars;
省略部分代码
}
p *$6取地址获得成员变量列表ivar_list_tp $7.get(0)打印成员变量
获取类方法
x/4gx LGPerson.class,打印LGPerson的内存,得到前8个字节0x00000001000083c0p/x 0x00000001000083c0 & 0x00007ffffffffff8ULL,获得元类地址- 后面步骤和获得实例方法一致(略)
总结
ISA走位:实例对象的ISA指向类对象,类对象的ISA指向其元类,元类的ISA指向根元类,根元类的ISA指向其自身- 继承链:
-
- 子类继承自父类,父类继承自根类,根类继承
nil
- 子类继承自父类,父类继承自根类,根类继承
-
- 子元类继承自父元类,父元类继承自根元类,根元类继承自根类
- 类的结构:
ISA、superclass、cache、bits - 获取类的属性:获取类的首地址->偏移
32位拿到class_data_bits_t->data()->properties()->property_list_t->get() - 获取类的实例方法:获取类的首地址->偏移
32``位拿到class_data_bits_t->data()->methods()->method_list_t->get().big() - 获取类的成员变量:获取类的首地址->偏移
32位拿到class_data_bits_t->data()->ro()->ivar_list_t->get() - 获取类方法:获取元类地址->偏移
32``位拿到class_data_bits_t->data()->methods()->method_list_t->get().big()