isa 流程探索
第一步:新建两个类QHPerson
和QHTearcher
,其中QHTearcher
继承QHPerson
,然后我们做如下操作:
isa探索.png
通过打印不断的打印isa,我们发现可以总结出:
探索2.png
我们发现有两个地址我们不知道是啥:
0x0000000100008150
,0x00007fff8892eca0
。实际上这两个一个是QHPerson
的元类,出现循环的是根元类
验证
我们通过查看macho
的string table
标可以发现
探索3.png
继承链
通过class_getSuperclass
来获取父类
探索4.png
我们可以看到
QHTearcher
-> QHPerson
->NSObject
-nil
元类链
//teacher 的元类
Class teachMetaClass = objc_getMetaClass("QHTeacher");
NSLog(@"teacher 的元类:%@ - %p",teachMetaClass,teachMetaClass);
//person的元类
Class personMetaClass = objc_getMetaClass("QHPerson");
NSLog(@"person的元类:%@ - %p",personMetaClass,personMetaClass);
Class superTeachMetaClass = class_getSuperclass(teachMetaClass);
NSLog(@"teacher 的元类的父类:%@ - %p",superTeachMetaClass,superTeachMetaClass);
Class superpersonMetaClass = class_getSuperclass(personMetaClass);
NSLog(@"person的元类的父类:%@ - %p",superpersonMetaClass,superpersonMetaClass);
//
Class nsobectClass = object_getClass(NSObject.class);
NSLog(@"nsobectClass:%@ - %p",nsobectClass,nsobectClass);
打印结果如下:
探索6.png
从打印结果我们可以发现
QHTeacher的元类
->QHPerson的元类
->NSObject
->nil
总结
通过以上的探索可以总结出非常经典的isa走位图
isa流程图.png
类的结构分析
首先分析Class
,打开objc
源码发现
typedef struct objc_class *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
结构体
内存偏移
现在我们要探索一下bits存储的数据,isa
(8个字节)superclass
(8个字节),cache_t
(16字节),也就是总共偏移32个字节。
类的属性存储
第一张.png
通过
lldb
找到了bit
地址
通过源码查看
class_data_bits_t
结构
struct class_data_bits_t {
...
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
...
}
查看class_rw_t
结构
struct class_rw_t {
...
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is()) {
return v.get(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get(&ro_or_rw_ext)->baseProperties};
}
}
...
}
properties()
、 methods()
这两个方法可以获取属性
通过lldb验证属性
第二张.png
通过lldb验证方法
第三张.png
成员变量存储
从之前的properties里面,我们并没有发现成员变量,那么成员变量存储在哪呢?
我们用lldb同样的方式继续,通过源码我们发现class_rw_t
有一个ro
,通过lldb打印
探索7.png
ro
里面有ivars
正好是我们的成员变量
探索8.png
类方法的存储
1,通过上面的探索methods
发现,这里里面好像并
没有找到类方法,那没类方法究竟存储在哪?
2.实例方法存在类中,那么类方法有没有可能存在元类中,我们用lldb验证一下
探索9.png
通过
methods
获取方法
探索10.png
总结
1.类
在底层实际上是一个结构体objc_class
. objc_class
实际上也是继承objc_object
,所以类
实际上也是对象
2.对象
的方法
、属性
实际上存在类的class_data_bits_t
的class_rw_t
里面
3.成员变量存在ro中
4.实例
方法存在类
中,类方法
存在元类
中
补充 实例和类isKindOfClass
和isMemberOfClass
探索
代码如下
void testKindOfMethod ()
{
//类方法
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]]; //
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]]; //
BOOL re3 = [(id)[QHPerson class] isKindOfClass:[QHPerson class]]; //
BOOL re4 = [(id)[QHPerson class] isMemberOfClass:[QHPerson class]]; //
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
//对象方法
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]]; //
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]]; //
BOOL re7 = [(id)[QHPerson alloc] isKindOfClass:[QHPerson class]]; //
BOOL re8 = [(id)[QHPerson alloc] isMemberOfClass:[QHPerson class]]; //
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
}
打印结果:
打印结果.png
看到打印结果是不是和打印不一致,那我们现在就看看
objc
源码:
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
对比isa走位图,然后分析
-
NSobject
t 的isa
是根元类
,根元类
的父类是NSObject
,所以ret1
为ture
-
NSobject
t 的isa
是根元类
,NSObject 和根元类不相等 结果为false
- 遍历,
QHPerson
不等于元类、也不等于根元类,也不等于NSObject
, 结果为false
- 同理
QHPerson
不等于QHPerson
的元类,结果为false
- 遍历第一次,
NSObjcet
等于NSObject
tcls
=cls
,为ture
同理ret6 、ret7、ret8 都为true