iOS底层-isa指针、class结构
isa指针
- isa指针
- instance的isa指向class
- 当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
- class的isa指向meta-class
- 当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
- instance的isa指向class
- superclass指针
- class对象的superclass指针
-
当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用
-
- meta-class对象的superclass指针
-
Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用
-
- class对象的superclass指针
- isa、superclass总结
- instance的isa指向class
- class的isa指向meta-class
- meta-class的isa指向基类的meta-class
- 基类meta-class的isa指向它自己(基类meta-class)
- class的superclass指向父类的class
- 如果没有父类,superclass指针为nil
- meta-class的superclass指向父类的meta-class
- 基类的meta-class的superclass指向基类的class
- instance调用对象方法的轨迹
- isa找到class,方法不存在,就通过superclass找父类
- class调用类方法的轨迹
- isa找meta-class,方法不存在,就通过superclass找父类
- 疑问:
- 通过上面的图,如过一个类调用一个类方法,首先通过当前类的isa指针到当前类的元类中去找,找不到,会通过superclass指针,到其父类的元类中去找,如果父类此时是根类元类,而且根类元类中也没有该类方法,那么按照上图的分析
- 根类元类会通过它的superclass指针找到它的(根类)类对象,那么问题来了? 类对象中只有对象方法呀? 难道会调用对象方法?
- 结论:会到根类类对象中寻找同名的对象方法,有就调用,没有就报错
- 也就是说,类对象不一定会调用类方法,也有可能调用对象方法
-
举例证明:
//NSObject的分类 #import <Foundation/Foundation.h> @interface NSObject (Test) +(void)test; @end #import "NSObject+Test.h" @implementation NSObject (Test) //+(void)test{ // NSLog(@"NSObject====+test ===%p",self); //} -(void)test{ NSLog(@"NSObject====-test ===%p",self); } @end //main文件 #import <Foundation/Foundation.h> #import "NSObject+Test.h" @interface Person : NSObject +(void)test; @end @implementation Person //+(void)test{ // NSLog(@"Person====+test ===%p",self); //} @end int main(int argc, const char * argv[]) { @autoreleasepool { NSLog(@"Person=====%p",[Person class]); NSLog(@"NSObject======%p",[NSObject class]); [Person test]; [NSObject test]; } return 0; } //打印 2019-05-06 11:28:52.147363+0800 03-isa与superclass[3060:191786] Person=====0x1000011e0 2019-05-06 11:28:52.147779+0800 03-isa与superclass[3060:191786] NSObject======0x7fff9840a140 2019-05-06 11:28:52.147796+0800 03-isa与superclass[3060:191786] NSObject====-test ===0x1000011e0 2019-05-06 11:28:52.147808+0800 03-isa与superclass[3060:191786] NSObject====-test ===0x7fff9840a140
- 从打印可以看出,调用的是
-test
,调用了对象方法
- 从打印可以看出,调用的是
- isa指针的值
-
person对象的isa指向它的类对象,那么下面验证一下:
@interface Person : NSObject @end @implementation Person @end int main(int argc, const char * argv[]) { @autoreleasepool { Person *person = [[Person alloc] init]; Class personclass = [Person class]; Class personMetaClass = object_getClass(personclass); /** 通过打断点,输入lldb命令,获取到person对象的isa值 p/x person->isa 输出:0x001d8001000014d1 //获取到类对象的地址值 p/x personclass 输出:0x00000001000014d0 会发现,person的isa指向的地址,并不等于类对象的地址。但是如果做如下操作,则会相等 p/x 0x001d8001000014d1 & 0x0000000ffffffff8ULL 输出:0x00000001000014d0 因此,person对象的isa指针不是直接等于类对象的地址,而是需要做一个 & ISA_MASK运算后,才等于类对象的地址。 */ NSLog(@"%p===%p===%p",person,personclass,personMetaClass); } return 0; }
-
通过找源码objc4,获取到如下宏定义
# if __arm64__ # define ISA_MASK 0x0000000ffffffff8ULL # elif __x86_64__ # define ISA_MASK 0x00007ffffffffff8ULL # endif
-
-
Class的结构
- Xcode中的Class结构
-
Command右击Class,进入
/// An opaque type that represents an Objective-C class. typedef struct objc_class *Class; //OC中的类 Class /// Represents an instance of a class. //一个类的实例对象 struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY; }; /// A pointer to an instance of a class. //指向一个实例的指针 typedef struct objc_object *id;
- Class 本质是一个结构体指针 :objc_class类型的结构体指针
- 实例对象本质是一个结构体: objc_object类型的结构体,包含一个成员变量isa
- id 就是objc_object类型的结构体指针
-
Command右击objc_class,进入
struct objc_class { Class _Nonnull isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__ Class _Nullable super_class OBJC2_UNAVAILABLE; const char * _Nonnull name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
- 会发现
#if !__OBJC2__
说明,OC-2.0已经过期了 - 而目前所有的Xcode都是OC-2.0因此,这个不能分析问题
- 会发现
-
- 源码中的Class结构
- 下载源码: https://opensource.apple.com/tarballs/objc4/
-
XCode打开源码,搜索
struct objc_class
,定位到objc-runtime-new.h文件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() { return bits.data(); } void setData(class_rw_t *newData) { bits.setData(newData); } ....成员函数.... }
- 分析源码
- 源码是用C++写的,因此需要懂C++
- C++中,struct定义的就是一个类,不在是结构体,跟class定义的类基本一样
- 下面遇到struct类型就以类分析了,不按照结构体分析了
-
可以看出objc_class类继承自objc_object,那么进入objc_object查看
union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } Class cls; uintptr_t bits; }; struct objc_object { private: //从上面的isa_t的联合体看出,isa_t跟基本就是Class //因此也可以写成: Class isa: isa_t isa; public: // ISA() assumes this is NOT a tagged pointer object Class ISA(); // getIsa() allows this to be a tagged pointer object Class getIsa(); ...成员函数... }
-
上面基本上可以写成如下(不考虑成员函数的情况下)
struct objc_class { Class isa; Class superclass; cache_t cache; class_data_bits_t bits; }
- 那么问题来了,貌似少了很多信息,对象方法、类方法,存放哪里呢?
-
点击objc_class的成员函数data()的返回值class_rw_t进入查看
struct class_rw_t { uint32_t flags; uint32_t version; const class_ro_t *ro; method_array_t methods; property_array_t properties; protocol_array_t protocols; Class firstSubclass; Class nextSiblingClass; char *demangledName; };
-
可以看到class_rw_t这个类中存放这方法列表、属性列表等信息,但是发现却少了成员列表,进入class_ro_t
struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; #ifdef __LP64__ uint32_t reserved; #endif 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这个类,需要通过objc_class的成员函数data()获取,那么,data()内部实际是什么呢?
class_rw_t *data() { return bits.data(); }
-
可以返现需要调用bits对象的data()成员函数就可以了,bits我们知道是class_data_bits_t类型,进入该类型,找到该成员函数如下:
class_rw_t* data() { //注意,此时的bits,仅仅是class_data_bits_t类的一个成员变量 // Values are the FAST_ flags above. //uintptr_t bits; return (class_rw_t *)(bits & FAST_DATA_MASK); }
-
因此,我们只要拿到objc_class类的bits成员变量,然后bits.bits & FAST_DATA_MASK,即可获取class_rw_t数据
-
-
精简图如下:
- 源码是用C++写的,因此需要懂C++
相关面试题
- 对象的isa指针指向哪里?
- instance对象的isa指向class对象
- class对象的isa指向meta-class对象
- meta-class对象的isa指向基类的meta-class对象
- OC的类信息存放在哪里?
- 对象方法、属性、成员变量、协议信息,存放在class对象中
- 类方法,存放在meta-class对象中
- 成员变量的具体值,存放在instance对象