isa的作用与内部结构(上)

一、回顾

oc对象的本质就是一个结构体,结构体的内部包含一个isa指针

指针:是一种保存变量地址的变量。

//OC
@interface NSObject{
  Class isa; // 包含一个isa指针(地址值)
}
// 对应的 C
struct NSObject_IMPL{
  Class isa;
}
//typedef起别名 Class是指向objc_class的指针 
typedef struct objc_class *Class; 
isa->Class->objc_class isa保存的地址存储着obj_class结构体
// isa 本质就是一个指向 objc_class 结构体的指针

二、实例对象的isa

实例对象保存了变量的值,没有保存方法等其他的信息,那么实例对象调用方法的时候,是如何找到正确的方法呢?

isa 将类对象的地址保存到isa中 ,调用方法的时候,通过isa寻找到类对象,然后调用类对象中保存的方法

同理类对象的isa指向元类对象,可以获取到元类对象中保存的类方法

三、元类对象的isa

元类对象后面只剩下基类的元类对象,所以每个元类对象的isa都是直接指向基类 即 NSObject 的元类对象,而基类的元类对象指向它自己。


image.png

四、举例证明

 // 实例化
 Person *p = [[Person alloc] init];
// 获取类对象地址
Class person_class = [Person class];
// 获取元类对象的地址
Class person_meta_class = object_getClass(person_class);
// 打印p->isa 
(lldb) p/x (long)p->isa  //x表示转化为十六进制
(long) $0 = 0x001d800100001149
// 打印 person_class地址,查看p->isa是否已person_class地址一致
(lldb) p/x person_class
(Class) $1 = 0x0000000100001148 Person

两次打印结果不一致
在64位之后,isa的内存地址需要&一个掩码值,才能获取到真正的内存地址

# if __arm64__ 
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
(lldb) p/x 0x001d800100001149 & 0x00007ffffffffff8
(long) $4 = 0x0000000100001148
(lldb) p/x person_class
(Class) $1 = 0x0000000100001148 Person
这次确实两次地址值一致

由此可见实例对象的isa确实是指向了类对象的内存地址
若要证明 类对象的isa指向元类对象,需要自定义一个objc_class

struct pf_objc_class {
    Class isa;
};
// 将类对象转化为pf_objc_class结构体,才能获取到类对象的isa
struct pf_objc_class *person_class2 = (__bridge struct pf_objc_class *)(person_class);
// 类对象的isa地址
(lldb) p/x person_class2->isa
(Class) $5 = 0x001d800100001121
// 元类对象的实际地址
(lldb) p/x person_meta_class
(Class) $6 = 0x0000000100001120
// 同样将类对象的isa值&掩码值后等于元类对象的地址
(lldb) p/x 0x001d800100001121 & 0x00007ffffffffff8
(long) $7 = 0x0000000100001120

举例证明isa变量的内存地址,就是对象的内存地址

(lldb) p p // 打印p的地址
(Person *) $9 = 0x0000000100541d80
(lldb) p &p->isa // 打印isa的地址(不是isa的值)
(__unsafe_unretained Class *) $7 = 0x0000000100541d80
/// 所谓的内存地址,就是他们占用内存的第一个位置
///如对象p占用内存16个字节,是从0x0000000100541d80开始,0x0000000100541d90结束,对象都有固定的长度,所以只需要记录开始位置
/// 即 isa内存起始位置就是对象p的起始位置

五、总结

  1. isa的内存地址就是对象的内存地址
  2. 实例对象的isa的值是类对象地址
  3. 类对象的isa的值是元类对象地址
  4. 元类对象的isa值是基类的元类对象地址,基类的元类对象isa的值是它自己的地址
思考:isa仅仅是保存地址这一个作用吗?64位之后,优化了什么?

isa的作用与内部结构(下)

你可能感兴趣的:(isa的作用与内部结构(上))