一、前言
在我的上一篇博客iOS isa初步理解之实例的isa说到,通过[NSObject alloc]
创建的实例对象,其实就是objc_object
的实例,而其中的isa
指向的是由objc_class
创建的类对象。并且还有几点细节,分别是
1、
objc_class
是objc_object
的子类;
2、objc_class
除了由objc_object
继承带来的isa
,还是自己的superClass
指针; (其实还有其他一些成员变量如cache
和bits
,不过我们这里先不做讨论)
Note:希望大家在看这篇文章的时候对 对象(object)
、 类(class)
有一个比较清晰的认识。
二 准备
1、代码准备,我们先准备一个小iOS工程test
,在main.m中加入如下代码
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
@interface CCPerson : NSObject
@end
@implementation CCPerson
@end
@interface CCTeacher : CCPerson
@end
@implementation CCTeacher
@end
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
NSObject *object_instance = [NSObject alloc];
CCPerson *person = [CCPerson alloc];
CCTeacher *teacher = [CCTeacher alloc];
NSLog(@"%p-%p-%p", object_instance, person, teacher);
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
2、我们找到objc源码下载一份分析。截取objc.h
里面的部分代码如下
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *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;
以及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() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
....
};
三、分析
1、运行我们的test
小工程,得到如下结果:
test[4049:1074051] 0x281bcc020-0x281bcc060-0x281bcc0a0
(lldb)
打印出我么实例化三个对象的首地址
2、打印一下三个地址对应的内存内容
(lldb) x/gx 0x281bcc020
0x281bcc020: 0x000001a1d0de5219
(lldb) x/gx 0x281bcc060
0x281bcc060: 0x000001a1025215a9
(lldb) x/gx 0x281bcc0a0
0x281bcc0a0: 0x000001a1025215f9
(lldb)
根据我们objc_object
struct结构得知,对应的内存内容应该为Class
内容,是一个isa
,但是这里的isa
是经过初始化后的isa
(即包含指向Cls
的信息和一些其余信息)。我们可以通过& 0x0000000ffffffff8ULL
取出对应的isa
指向的Class
信息如下
(lldb) p/x 0x000001a1d0de5219 & 0x0000000ffffffff8ULL
(unsigned long long) $0 = 0x00000001d0de5218
(lldb) p/x 0x000001a1025215a9 & 0x0000000ffffffff8ULL
(unsigned long long) $1 = 0x00000001025215a8
(lldb) p/x 0x000001a1025215f9 & 0x0000000ffffffff8ULL
(unsigned long long) $2 = 0x00000001025215f8
(lldb)
对应是的$0 $1 $2
就是isa指向的内容,不信的话我们po
一下,截图如下
这个不就是我们定义的三个类吗?
结论1:实例对象
的isa
指向的内容是对应的类对象
信息
3、我们知道objc_class
是OC类
(即我们的类对象)的在C++层面的定义,它又继承于objc_object
,所以类对象
里面应该也包含isa
的信息.
现在,我们已经获取到了类对象的首地址,那么我们就直接看看类对象的内存分布吧;对应命令和结果如下
不知道大家有没有发现我剪头所指向的内容居然和自己一模一样。
我们知道
objc_class
第二个属性就是类型为Class
的superclass
结论2:类对象
的superclass
指向父类
对应的类对象
4、我们现在po
一下类对象
对应的isa
(这里不需要& 0x0000000ffffffff8ULL
,因为结果一样,自己证明即可)
不足知道大家有没有发现居然有两个类;其实这就是我们接下来要说的
元类(metalClass)
。我们接下来再看一下元类
对应的内存结构
(lldb) x/2gx 0x00000001d0de51f0 //NSObject元类地址
0x1d0de51f0: 0x00000001d0de51f0 0x00000001d0de5218 // 根元类地址 + 父类地址(居然等于NSObject类对象的地址)
(lldb) x/2gx 0x0000000102521580 //CCPerson元类地址
0x102521580: 0x00000001d0de51f0 0x00000001d0de51f0 // 根元类地址 + 父类地址(=CCPerson父类的元类地址)
(lldb) x/2gx 0x00000001025215d0 //CCTeacher元类地址
0x1025215d0: 0x00000001d0de51f0 0x0000000102521580 // 根元类地址 + 父类地址(=CCTeacher父类的元类地址)
(lldb)
根据上面的结果以及我后面添加的注释,可得下面结论
结论3:所有元类的isa
都指向NSObject 对应的元类
我们称其为根元类
结论4:根元类的superclass
指针指向NSObject
的类对象,其他子元类的superclass
指针指向对应父类的元类。
四、总结
根据
结论1:
实例对象
的isa
指向的内容是对应的类对象
信息
结论2:类对象
的superclass
指向父类
对应的类对象
结论3:所有元类的isa
都指向NSObject 对应的元类
我们称其为根元类
结论4:根元类的superclass
指针指向NSObject
的类对象,其他子元类的superclass
指针指向对应父类的元类。
可得出如下经典的图,也是iOS十分重要的一个图。
isa流程图.png
网友评论