iOS isa深入理解之isa指向流程

2020-09-12  本文已影响0人  Johnny_Z

一、前言

在我的上一篇博客iOS isa初步理解之实例的isa说到,通过[NSObject alloc]创建的实例对象,其实就是objc_object的实例,而其中的isa指向的是由objc_class创建的类对象。并且还有几点细节,分别是

1、objc_classobjc_object的子类;
2、objc_class除了由objc_object继承带来的isa,还是自己的superClass指针; (其实还有其他一些成员变量如cachebits,不过我们这里先不做讨论)

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_objectstruct结构得知,对应的内存内容应该为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

这个不就是我们定义的三个类吗?

结论1:实例对象isa指向的内容是对应的类对象信息

3、我们知道objc_classOC类(即我们的类对象)的在C++层面的定义,它又继承于objc_object,所以类对象里面应该也包含isa的信息.
现在,我们已经获取到了类对象的首地址,那么我们就直接看看类对象的内存分布吧;对应命令和结果如下

图2
不知道大家有没有发现我剪头所指向的内容居然和自己一模一样。
我们知道objc_class第二个属性就是类型为Classsuperclass

结论2:类对象superclass指向父类对应的类对象

4、我们现在po一下类对象对应的isa(这里不需要& 0x0000000ffffffff8ULL,因为结果一样,自己证明即可)

图3
不足知道大家有没有发现居然有两个类;其实这就是我们接下来要说的元类(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
上一篇 下一篇

猜你喜欢

热点阅读