美文网首页
iOS-底层原理-类&类结构分析 isa--superclass

iOS-底层原理-类&类结构分析 isa--superclass

作者: 如意神王 | 来源:发表于2022-05-19 15:37 被阅读0次

    本文的主要目的是分析 类 & 类的结构,整篇都是围绕一个展开的一些探索

    1.类的分析之Class isa和Class superclass ---> isa走向和继承关系链

    类的分析 主要是分析 isa的走向 以及 类之间的继承关系,注意对象之间没有继承关系

    分析类之前提出几个问题
    1.Class到底是什么?Class之间的关系?
    2.对象isaisa之间的区别?分别存储的是什么值?
    3.对象方法存储在中,那类方法存储在哪里呢?如果一直有相应的来存储那么尽头在哪里呢?
    4.isasupperClass是什么关系? isa到底有哪些作用?
    5.在内存中到底存在几份

    类的底层定义,runtime.h里早期类的定义,现在已经弃用

    struct objc_class {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY; // isa
    
    #if !__OBJC2__
        Class _Nullable super_class                              OBJC2_UNAVAILABLE; // super_class 父类指针
        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;
    /* Use `Class` instead of `struct objc_class *` */
    

    objc_object的部分代码

    /// Represents an instance of a class.
    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    
    struct objc_object {
    private:
        isa_t isa;
    
    public:
    
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA(bool authenticated = false);
    
        // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
        Class rawISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
        
        uintptr_t isaBits() const;
    
    
        bool hasNonpointerIsa();
        bool isTaggedPointer();
        bool isTaggedPointerOrNil();
        bool isBasicTaggedPointer();
        bool isExtTaggedPointer();
        bool isClass();
    
        // object may have associated objects?
        bool hasAssociatedObjects();
        void setHasAssociatedObjects();
    
        // object may be weakly referenced?
        bool isWeaklyReferenced();
        void setWeaklyReferenced_nolock();
    
        // object may have -.cxx_destruct implementation?
        bool hasCxxDtor();
    
        // Optimized calls to retain/release methods
        id retain();
        void release();
        id autorelease();
    
        // Implementations of retain/release methods
        id rootRetain();
        bool rootRelease();
        id rootAutorelease();
        bool rootTryRetain();
        bool rootReleaseShouldDealloc();
        uintptr_t rootRetainCount();
    
        // Implementation of dealloc methods
        bool rootIsDeallocating();
        void clearDeallocating();
        void rootDealloc();
    };
    

    类的新的定义,部分代码, objc-runtime-new.h文件里面

    struct objc_class : objc_object {
        // Class ISA; // isa 继承自objc_object
        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);
        }
    }
    

    Class id宏定义

    typedef struct objc_class *Class;
    typedef struct objc_object *id;
    

    总结:
    1.Classobjc_class来自于底层objc_class结构体
    2.idobjc_object对象,即对象来自于objc_object结构体
    3.objc_class : objc_objectobjc_class继承自objc_object结构体,objc_classisa继承自objc_object,所以,和对象都有Class isa指针
    4.superclass有继承关系,对象没有superclass没有继承关系
    5.本篇博客研究类的 isa、superclass、bits三个成员变量,很有规律性,各个源码版本略有差异

    1.准备工作
    main.m里自定义类CJLPerson,代码如下

    // 协议 protocol
    @protocol CJLPersonDelegate <NSObject>
    //实例方法
    - (void)sendDataToController:(NSMutableArray *)dataArray;
    
    // 类方法
    + (void)sendString:(NSString *)string;
    
    // 可选实例方法
    @optional
    - (void)showKeyboard;
    
    // 可选类方法
    @optional
    + (void)closeKeyboard;
    
    @end
    
    @interface CJLPerson : NSObject <CJLPersonDelegate> {
        // 成员变量
        NSString * hobby;
        NSString * sex;
    }
    // 属性
    @property (nonatomic, copy) NSString * cjl_name;
    @property (nonatomic, copy) NSString * number;
    
    // 实例方法
    - (void)sayHello:(NSString *)hello;
    - (void)sayWorld;
    
    // 类方法
    + (void)sayBye;
    + (void)sayLove;
    
    @end
    
    @implementation CJLPerson
    
    - (void)sayHello:(NSString *)hello {
    
    }
    
    - (void)sayWorld {
    
    }
    
    + (void)sayBye {
    
    }
    
    + (void)sayLove {
    
    }
    
    - (void)sendDataToController:(NSMutableArray *)dataArray {
        
    }
    
    - (void)showKeyboard {
        
    }
    
    + (void)sendString:(NSString *)string {
        
    }
    
    

    调用方式

    
    int main(int argc, char * argv[]) {
        NSString * appDelegateClassName;
        @autoreleasepool {
            // Setup code that might create autoreleased objects goes here.
            appDelegateClassName = NSStringFromClass([AppDelegate class]);
            CJLPerson * person = [[CJLPerson alloc] init];
            NSLog(@"person == %@", person);
            
        }
        return UIApplicationMain(argc, argv, nil, appDelegateClassName);
    }
    
    

    @end

    2.元类

    首先,我们先通过一个案例的lldb调试先引入元类对象isa指向isa指向的就是元类
    通过断点调试,lldb输出如下,相信说明见注释

    我们由前面的内容知道对象的内存地址是第一个8字节是isa的值,其他后面的都是对应属性的值,假设都是NSString类型的属性,都是8字节,对象内存示意图如下

    对象的内存地址和内存分布

    {
        0x0100000104c01418 :Class isa 值 第一个8字节
        0x0100000104c01420 :name 值 第二个8字节
        0x0100000104c01428:height 值 第三个8字节
        0x0100000104c01430:weight 值 第四个8字节
        0x0100000104c01438:XXX   值  依次类推
    }
    

    类的源码结构

    struct objc_class : objc_object {
        Class ISA; // Class 类型 8字节 继承自objc_object, 第一8字节是isa
        Class superclass; // Class 类型 8字节, 第二8字节是superclass也就是父类
        cache_t cache;             // 16字节
        class_data_bits_t bits;    // 
        ...
    }
    

    以上可知

    1.对象的第一个8字节是isa
    2.的第一个8字节是isa
    3.的第二个8字节是supperclass,也就是父类
    4.对象没有supperclass,也就是说对象没有继承关系,只有继承关系

    (lldb) x/4gx person
    0x600003705e40: 0x0100000104c014d9 0x0000000000000000 //0x0100000104c014d9 对象的isa
    0x600003705e50: 0x0000000000000000 0x0000000000000000
    (lldb) p/x 0x0100000104c014d9 & 0x0000000ffffffff8ULL // 对象的isa &上ISA_MASK
    (unsigned long long) $1 = 0x0000000104c014d8 //类的16进制数值
    (lldb) po 0x0000000104c014d8 //打印类的16进制数值为CJLPerson
    CJLPerson
    
    (lldb) p/x CJLPerson.class //p/x直接打印类的16进制数值,可以看到与上面的一致
    (Class) $3 = 0x0000000104c014d8 CJLPerson
    
    (lldb) x/4gx 0x0000000104c014d8 //打印类的4段内存
    0x104c014d8: 0x0000000104c014b0 0x00000001c28fc628 //0x0000000104c014b0 类的isa
    0x104c014e8: 0x0001600003705e60 0x0001802100000000
    (lldb) p/x 0x0000000104c014b0 & 0x0000000ffffffff8ULL // 类的isa &上ISA_MASK
    (unsigned long long) $4 = 0x0000000104c014b0// 类的isa &上ISA_MASK的值与没有&上ISA_MASK完全一致
    // 说明类的isa 8字节64位中除了33位类信息之外其他全部为0
    
    (lldb) po 0x0000000104c014b0 // 打印类的isa指向输出结果依然是CJLPerson
    CJLPerson
    
    //0x0000000104c014d8 CJLPerson,名字相同,16进制数值不同,很明显是两个类,对象isa指向的是类,CJLPerson类本身
    //0x0000000104c014b0 CJLPerson,名字相同,16进制数值不同,很明显是两个类,类isa指向的是元类,
    //CJLPerson的元类,只是名字相同而已,暂且标记为CJLPerson_Metal
    // CJLPerson和CJLPerson的元类(CJLPerson_Metal)是两个不同的概念
    
    (lldb) x/4gx 0x0000000104c014b0  //打印CJLPerson的元类的4段内存
    0x104c014b0: 0x00000001c28fc600 0x00000001c28fc600
    0x104c014c0: 0x0003600002258080 0x0002e03500000000
    (lldb) po 0x00000001c28fc600 //输出CJLPerson_Metal的isa指向NSObject,此NSObject是NSObject的元类
    NSObject   // 即NSObject_Metal与NSObject是两个概念
    
    (lldb) x/4gx 0x00000001c28fc600 // 继续输出NSObject_Metal的isa指向,依然指向自身
    0x1c28fc600: 0x00000001c28fc600 0x00000001c28fc628
    0x1c28fc610: 0x00036000022580c0 0x0003e03400000000
    (lldb) x/4gx 0x00000001c28fc600 // 继续输出NSObject_Metal的isa指向,依然指向自身
    0x1c28fc600: 0x00000001c28fc600 0x00000001c28fc628
    0x1c28fc610: 0x00036000022580c0 0x0003e03400000000
    
    (lldb) p/x NSObject.class    //打印NSObject.class类本身
    (Class) $7 = 0x00000001c28fc628 NSObject
    (lldb) 
    
    //0x00000001c28fc628 NSObject
    //0x00000001c28fc600 NSObject元类(NSObject_Metal)
    // 这两个是不同的概念,不同的类
    
    

    define ISA_MASK 0x0000000ffffffff8ULL
    根据调试过程,我们产生了一个疑问:为什么图中的p/x 0x0100000104c014d9 & 0x0000000ffffffff8ULL与 p/x 0x0000000104c014b0 & 0x0000000ffffffff8ULL 中的类信息打印出来的类名都是CJLPerson,这两个CJLPerson是什么关系呢?

    0x0100000104c014d9person对象的isa指针地址,其&后得到的结果是 创建person的类CJLPerson
    0x0000000104c014b0CJLPerson类的isa的所存储的指针地址,即 CJLPerson类的类的地址,在Apple中,我们简称CJLPerson类的类为元类

    0x0100000104c014d9 & 0x0000000ffffffff8ULL = 0x0000000104c014d8(或者是首地址不同) = CJLPerson类名相同,这是CJLPerson类
    0x0000000104c014b0 & 0x0000000ffffffff8ULL = 0x0000000104c014b0(或者是首地址不同) = CJLPerson类名相同,这是CJLPerson元类,暂且标记为CJLPerson _metaClass

    CJLPerson类CJLPerson _metaClass是两个不同的,且每个在内存中只有一份,且内存地址不同,

    元类是两个不同的概念,只是体现出来的类名字相同而已

    所以,两个打印都是CJLPerson的根本原因就是因为元类导致的

    3.元类的说明

    下面来解释什么是元类,主要有以下几点说明:

    我们都知道 对象isa 是指向其实也是一个对象,可以称为类对象,其isa指向苹果定义的元类

    元类系统给的,其定义创建都是由编译器完成,在这个过程中,的归属来自于元类

    元类 是类对象 的类,每个都有一个独一无二的元类用来存储 类方法的相关信息。

    元类本身是没有名称的,由于与相关联,所以使用了同名一样的名称

    下面通过lldb命令来探索元类的走向,也就是isa的走位,如下图所示,可以得出一个关系链:

    对象isa ---> 类isa ---> 元类isa ---> NSobject(根元类) ---> NSobject(根元类)

    person(对象) ---> CJLPerson(类) ---> CJLPerson _metaClass(CJLPerson元类) ---> NSobject_metaClass (NSobject根元类)--->NSobject_metaClass(根元类自身)

    CJLPerson不是CJLPerson_metaClass(CJLPerson元类),内存地址不同,但是每一个只有一份
    NSobject不是NSobject_metaClass(根元类),内存地址不同,但是每一个只有一份

    4.isa走位图

    isa走位图.jpeg

    5.LLDB调试 isa走位图

    Xcode截图isa指向.jpeg

    6.superclass指向

    superclass走势图.jpeg

    7.LLDB调试 superclass指向

    Xcode截图superclass指向.jpeg

    8.苹果官方的isa走位以及继承关系图

    isa流程图.png

    9.LLDB调试 CJLTeacher ---> CJLPerson ---> NSObject 三级继承关系isa走位及superclass指向

    新增类CJLTeacher继承于CJLPersonCJLPerson继承于NSObject,调用如下

    @interface CJLTeacher : CJLPerson
    
    @end
    
    @implementation CJLTeacher
    
    @end
    
    int main(int argc, char * argv[]) {
        NSString * appDelegateClassName;
        @autoreleasepool {
            // Setup code that might create autoreleased objects goes here.
            appDelegateClassName = NSStringFromClass([AppDelegate class]);
            // ISA_MASK        0x0000000ffffffff8ULL
     
            CJLTeacher * teacher = [[CJLTeacher alloc] init];
            CJLPerson * person = [[CJLPerson alloc] init];
            NSObject * obj = [[NSObject alloc] init];
            NSLog(@"teacher == %@ person == %@ obj == %@", teacher, person, obj);
        }
        return UIApplicationMain(argc, argv, nil, appDelegateClassName);
    }
    
    

    LLDB调试,完美验证iOS系统的isa走位及继承关系图

    Teacher_Person_NSObject三级继承关系isa和superclass.jpeg

    10. clang 编译位cpp文件 查看isa 和 superclass指向

    CJLTeacher以及其元类isasuperclass指向

    CJLTeacher以及元类isa和superclass指向.jpeg

    CJLPerson以及其元类isasuperclass指向

    CJLPerson以及元类isa和superclass指向.jpeg

    NSObject根元类被编译成OBJC_METACLASS_$_NSObject
    CJLPerson元类被编译成OBJC_METACLASS_$_CJLPerson
    CJLTeacher元类被编译成OBJC_METACLASS_$_CJLTeacher
    NSObject被编译成OBJC_CLASS_$_NSObject
    CJLPerson被编译成OBJC_CLASS_$_CJLPerson

    static void OBJC_CLASS_SETUP_$_CJLTeacher(void ) {
        OBJC_METACLASS_$_CJLTeacher.isa = &OBJC_METACLASS_$_NSObject;
        OBJC_METACLASS_$_CJLTeacher.superclass = &OBJC_METACLASS_$_CJLPerson;
        OBJC_METACLASS_$_CJLTeacher.cache = &_objc_empty_cache;
        OBJC_CLASS_$_CJLTeacher.isa = &OBJC_METACLASS_$_CJLTeacher;
        OBJC_CLASS_$_CJLTeacher.superclass = &OBJC_CLASS_$_CJLPerson;
        OBJC_CLASS_$_CJLTeacher.cache = &_objc_empty_cache;
    }
    
    static void OBJC_CLASS_SETUP_$_CJLPerson(void ) {
        OBJC_METACLASS_$_CJLPerson.isa = &OBJC_METACLASS_$_NSObject;
        OBJC_METACLASS_$_CJLPerson.superclass = &OBJC_METACLASS_$_NSObject;
        OBJC_METACLASS_$_CJLPerson.cache = &_objc_empty_cache;
        OBJC_CLASS_$_CJLPerson.isa = &OBJC_METACLASS_$_CJLPerson;
        OBJC_CLASS_$_CJLPerson.superclass = &OBJC_CLASS_$_NSObject;
        OBJC_CLASS_$_CJLPerson.cache = &_objc_empty_cache;
    }
    

    2.类的分析之类信息存储 ---> bits( class_data_bits_t 结构体类 )

    以上研究了类的isa走势图和superclass继承关系图,那么类的其他信息比如方法、协议、属性、成员变量存在什么地方呢?

    cache_t cache 是方法缓存
    很明显类的相关信息是存在class_data_bits_t bits这个结构体成员变量里面的
    下面探索 bits里面怎么存储了方法、协议、属性、成员变量等信息的,各个需要用到的结构体的部分代码如下,精简便于理解,不同版本略有差异,但是流程是一样的

    1.objc_class 类结构体部分代码

    struct objc_class : objc_object {
        // Class ISA;    // --> 继承自objc_object --> 8字节
        Class superclass;  // --> superclass 父类指针--> 8字节
        cache_t cache;             // 方法缓存 formerly cache pointer and vtable  --> 16字节
        class_data_bits_t bits;    // 类信息存储 class_rw_t * plus custom rr/alloc flags
        class_rw_t *data() const {
            return bits.data();
        }
    }
    
    1. class_data_bits_t 结构体部分代码
    struct class_data_bits_t {
        // Values are the FAST_ flags above.
        uintptr_t bits;
    public:
        class_rw_t* data() const {
            return (class_rw_t *)(bits & FAST_DATA_MASK);
        }
    };
    
    
    1. class_rw_t 结构体部分代码
    struct class_rw_t {
    public:
        const class_ro_t *ro() const {   //ro()  ---> 获取存储成员变量的相关结构体 ---> class_ro_t
            auto v = get_ro_or_rwe();     // class_ro_t ---> 获取成员变量方法列表
            if (slowpath(v.is<class_rw_ext_t *>())) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
            }
            return v.get<const class_ro_t *>(&ro_or_rw_ext);
        }
    
        const method_array_t methods() const {    //methods()函数 ---> 获取方法列表
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
            } else {
                return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
            }
        }
    
        const property_array_t properties() const {   //properties()  ---> 获取属性列表
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
            } else {
                return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
            }
        }
    
        const protocol_array_t protocols() const {    //获取协议列表 
            auto v = get_ro_or_rwe();
            if (v.is<class_rw_ext_t *>()) {
                return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
            } else {
                return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
            }
        }
    };
    
    1. method_t 结构体部分代码
    struct method_t {
    
        struct big {  // method_t 内部又重新定义了一个 struct big 三个成员变量 SEL---types---IMP  //同 method_t
            SEL name;
            const char *types;
            MethodListIMP imp; // using MethodListIMP = IMP;
        };
    
    public:
    
       big &big() const { 
            ASSERT(!isSmall());
            return *(struct big *)this;
        }
    
        SEL name() const { // name() ---> 获取SEL
            if (isSmall()) {
                return (small().inSharedCache()
                        ? (SEL)small().name.get()
                        : *(SEL *)small().name.get());
            } else {
                return big().name;
            }
        }
        const char *types() const { // types() ---> 获取方法签名
            return isSmall() ? small().types.get() : big().types;
        }
        IMP imp(bool needsLock) const {  // imp(1) ---> 获取IMP
            if (isSmall()) {
                IMP imp = remappedImp(needsLock);
                if (!imp)
                    imp = ptrauth_sign_unauthenticated(small().imp.get(),
                                                       ptrauth_key_function_pointer, 0);
                return imp;
            }
            return big().imp;
        }
    };
    
    1. property_t结构体全部
    struct property_t {
        const char *name; // 名称
        const char *attributes; // 特性
    };
    
    1. class_ro_t结构体部分代码
    struct class_ro_t {
        const ivar_list_t * ivars; // 成员变量列表
    }
    
    1. ivar_t结构体部分代码
    struct ivar_t {
        const char *name; //成员变量名称
        const char *type; // 成员变量类型
    };
    

    8.protocol_t : objc_object结构体部分代码,继承自objc_object

    struct protocol_t : objc_object {
        const char *mangledName; // 协议名称
        struct protocol_list_t *protocols;
        method_list_t *instanceMethods; // 实例方法列表
        method_list_t *classMethods; // 类方法列表
        method_list_t *optionalInstanceMethods;// 可选实例方法列表
        method_list_t *optionalClassMethods;// 可选类方法列表
        property_list_t *instanceProperties;
        uint32_t size;   // sizeof(protocol_t)
        uint32_t flags;
    };
    

    核心思路 :

    对应的结构体---> 函数(解析内存结构) ---> 获取到其他结构体 ---> 函数(解析内存结构) ---> 需要的结构体 ---> 函数

    10.实例方法method获取流程

    结构体流程
    objc_class(类) ---> class_data_bits_t ---> class_rw_t --->method_t

    函数流程
    data() ---> methods()---> name() ---> 获取SEL
    data() ---> methods()---> types() ---> 获取types 函数签名
    data() ---> methods()---> imp(1) ---> 获取IMP

    整体流程
    objc_class(类) ---> class_data_bits_t ---> data() ---> class_rw_t ---> methods() ---> method_t

    method_t ---> name() --->SEL
    method_t ---> types() ---> 方法签名
    method_t --- >imp(1) ---> IMP

    11.类方法method获取流程

    结构体流程
    objc_class(元类) ---> class_data_bits_t ---> class_rw_t --->method_t

    函数流程
    data() ---> methods()---> name() ---> 获取SEL
    data() ---> methods()---> types() ---> 获取types 函数签名
    data() ---> methods()---> imp(1) ---> 获取IMP

    整体流程
    objc_class(元类) ---> class_data_bits_t ---> data() ---> class_rw_t ---> methods() ---> method_t

    method_t ---> name() --->SEL
    method_t ---> types() ---> 方法签名
    method_t --- >imp(1) ---> IMP

    12.属性property获取流程

    结构体流程
    objc_class(类) ---> class_data_bits_t ---> class_rw_t --->property_t

    函数流程
    data() ---> properties()---> name ---> 获取属性名称
    data() ---> properties()---> attributes ---> 获取属性特性attributes

    整体流程
    objc_class(类) ---> class_data_bits_t ---> data() ---> class_rw_t ---> properties() ---> property_t

    property_t ---> name ---> 获取属性名称
    property_t ---> attributes ---> 获取属性特性attributes

    13.成员变量ivar获取流程

    结构体流程
    objc_class(类) ---> class_data_bits_t ---> class_rw_t --->class_ro_t ---> ivar_t

    函数流程
    data() ---> ro()---> ivars ---> name 获取属性名称
    data() ---> ro()---> ivars ---> type 获取属性类型

    整体流程
    objc_class(类) ---> class_data_bits_t ---> data() ---> class_rw_t --->ro() ---> class_ro_t ---> ivar_t

    ivars ---> name 获取属性名称
    ivar_t ---> type ---> 获取属性类型

    14.协议protocol获取流程

    结构体流程
    objc_class(类) ---> class_data_bits_t ---> class_rw_t --->protocol_t(对象) ---> method_t

    函数流程
    data() ---> protocols()---> instanceMethods ---> 获取实例方法列表
    instanceMethods ---> name() --->SEL
    instanceMethods ---> types() --->方法签名
    instanceMethods ---> imp(1) --->IMP

    同上
    data() ---> protocols()---> classMethods ---> 获取类方法列表
    data() ---> protocols()---> optionalInstanceMethods ---> 获取获取可选实例方法
    data() ---> protocols()---> optionalClassMethods ---> 获取获取可选类方法

    整体流程
    objc_class(类) ---> class_data_bits_t ---> data() ---> class_rw_t ---> protocols() ---> protocol_t(对象) ---> instanceMethods

    instanceMethods ---> name() --->SEL
    instanceMethods ---> types() --->方法签名
    instanceMethods ---> imp(1) --->IMP

    15.LLDB调试实例方法method解析


    LLDB调试-bits实例方法method解析.jpeg

    16.LLDB调试类方法method解析


    LLDB调试-bits类方法method解析.jpeg

    17.LLDB调试属性property解析


    LLDB调试-bits属性property解析.jpeg

    18.LLDB调试成员变量ivar解析


    LLDB调试-bits-成员变量ivar解析.jpeg

    19.LLDB调试协议protocol解析


    LLDB调试-bits-协议protocol解析1.jpeg LLDB调试-bits-协议protocol解析2.jpeg

    20.LLDB调试-实例方法instanceMethod调试过程

    KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
    (lldb) p/x CJLPerson.class
    (Class) $0 = 0x00000001000081c0 CJLPerson
    (lldb) p (class_data_bits_t *) 0x00000001000081e0
    (class_data_bits_t *) $1 = 0x00000001000081e0
    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x0000000101119d10
    (lldb) p $2->methods()
    (const method_array_t) $3 = {
      list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
         = {
          list = {
            ptr = 0x0000000100003ea0
          }
          arrayAndFlag = 4294983328
        }
      }
    }
    (lldb) p $3.list
    (const method_list_t_authed_ptr<method_list_t>) $4 = {
      ptr = 0x0000000100003ea0
    }
    (lldb) p $4.ptr
    (method_list_t *const) $5 = 0x0000000100003ea0
    (lldb) p *$5
    (method_list_t) $6 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 5)
    }
    (lldb) p $6.get(0).name()
    (SEL) $7 = "sayHello:"
    (lldb) p $6.get(0).types()
    (const char *) $8 = 0x0000000100003f7a "v24@0:8@16"
    (lldb) p $6.get(0).imp(1)
    (IMP) $9 = 0x0000000100003be0 (KCObjcBuild`-[CJLPerson sayHello:] at main.m:34)
    (lldb) p $6.get(1).name()
    (SEL) $10 = "sayWorld"
    (lldb) p $6.get(1).types()
    (const char *) $11 = 0x0000000100003f66 "v16@0:8"
    (lldb) p $6.get(1).imp(1)
    (IMP) $12 = 0x0000000100003c1c (KCObjcBuild`-[CJLPerson sayWorld] at main.m:38)
    (lldb) p $6.get(2).name()
    (SEL) $13 = "cjl_name"
    (lldb) p $6.get(2).types()
    (const char *) $14 = 0x0000000100003f85 "@16@0:8"
    (lldb) p $6.get(2).imp(1)
    (IMP) $15 = 0x0000000100003c30 (KCObjcBuild`-[CJLPerson cjl_name] at main.m:24)
    (lldb) p $6.get(3).name()
    (SEL) $16 = "setCjl_name:"
    (lldb) p $6.get(3).types()
    (const char *) $17 = 0x0000000100003f7a "v24@0:8@16"
    (lldb) p $6.get(3).imp(1)
    (IMP) $18 = 0x0000000100003c54 (KCObjcBuild`-[CJLPerson setCjl_name:] at main.m:24)
    (lldb) p $6.get(4).name()
    (SEL) $19 = ".cxx_destruct"
    (lldb) p $6.get(5).name()
    Assertion failed: (i < count), function get, file /Users/objc M1和因特尔芯片源码/objc源码/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    (lldb)
    

    LLDB调试-类方法classMethod调试过程

    KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
    (lldb) p/x CJLPerson.class
    (Class) $0 = 0x0000000100008200 CJLPerson
    (lldb) x/2gx 0x0000000100008200
    0x100008200: 0x00000001000081d8 0x0000000100379140
    (lldb) po 0x00000001000081d8
    CJLPerson
    
    (lldb) p (class_data_bits_t *) 0x00000001000081f8
    (class_data_bits_t *) $2 = 0x00000001000081f8
    (lldb) p $2->data()
    (class_rw_t *) $3 = 0x0000000101029b40
    (lldb) p $3->methods()
    (const method_array_t) $4 = {
      list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
         = {
          list = {
            ptr = 0x0000000100003e28
          }
          arrayAndFlag = 4294983208
        }
      }
    }
    (lldb) p $4.list
    (const method_list_t_authed_ptr<method_list_t>) $5 = {
      ptr = 0x0000000100003e28
    }
    (lldb) p $5.ptr
    (method_list_t *const) $6 = 0x0000000100003e28
    (lldb) p *$6
    (method_list_t) $7 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 2)
    }
    (lldb) p $7.get(0).name()
    (SEL) $8 = "sayBye"
    (lldb) p $7.get(0).types()
    (const char *) $9 = 0x0000000100003f5a "v16@0:8"
    (lldb) p $7.get(0).imp(1)
    (IMP) $10 = 0x0000000100003adc (KCObjcBuild`+[CJLPerson sayBye] at main.m:42)
    (lldb) p $7.get(1).name()
    (SEL) $11 = "sayLove"
    (lldb) p $7.get(1).types()
    (const char *) $12 = 0x0000000100003f5a "v16@0:8"
    (lldb) p $7.get(1).imp(1)
    (IMP) $13 = 0x0000000100003af0 (KCObjcBuild`+[CJLPerson sayLove] at main.m:46)
    (lldb) p $7.get(2).name()
    Assertion failed: (i < count), function get, file /Users/objc M1和因特尔芯片源码/objc源码/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    (lldb)
    

    LLDB调试-属性property调试过程

    KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
    (lldb) p/x CJLPerson.class
    (Class) $0 = 0x0000000100008208 CJLPerson
    (lldb) p (class_data_bits_t *) 0x0000000100008228
    (class_data_bits_t *) $1 = 0x0000000100008228
    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x00000001008570f0
    (lldb) p $2->properties()
    (const property_array_t) $3 = {
      list_array_tt<property_t, property_list_t, RawPtr> = {
         = {
          list = {
            ptr = 0x0000000100008158
          }
          arrayAndFlag = 4295000408
        }
      }
    }
    (lldb) p $3.list
    (const RawPtr<property_list_t>) $4 = {
      ptr = 0x0000000100008158
    }
    (lldb) p $4.ptr
    (property_list_t *const) $5 = 0x0000000100008158
    (lldb) p *$5
    (property_list_t) $6 = {
      entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
    }
    (lldb) p $6.get(0).name
    (const char *) $7 = 0x0000000100003f1c "cjl_name"
    (lldb) p $6.get(0).attributes
    (const char *) $8 = 0x0000000100003eb7 "T@\"NSString\",C,N,V_cjl_name"
    (lldb) p $6.get(1).name
    (const char *) $9 = 0x0000000100003f32 "number"
    (lldb) p $6.get(1).attributes
    (const char *) $10 = 0x0000000100003ed3 "T@\"NSString\",C,N,V_number"
    (lldb) p $6.get(2).name
    Assertion failed: (i < count), function get, file /Users/objc M1和因特尔芯片源码/objc源码/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    (lldb)
    

    LLDB调试-成员变量ivar调试过程

    KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
    (lldb) p/x CJLPerson.class
    (Class) $0 = 0x0000000100008200 CJLPerson
    (lldb) p (class_data_bits_t *) 0x0000000100008220
    (class_data_bits_t *) $1 = 0x0000000100008220
    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x0000000105137810
    (lldb) p $2->ro()
    (const class_ro_t *) $3 = 0x0000000100008080
    (lldb) p $3->ivars
    (const ivar_list_t *const) $4 = 0x00000001000080c8
    (lldb) p *$4
    (const ivar_list_t) $5 = {
      entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
    }
    (lldb) p $5.get(0).name
    (const char *) $6 = 0x0000000100003ef5 "hobby"
    (lldb) p $5.get(0).type
    (const char *) $7 = 0x0000000100003f62 "@\"NSString\""
    (lldb) p $5.get(1).name
    (const char *) $8 = 0x0000000100003efb "sex"
    (lldb) p $5.get(1).type
    (const char *) $9 = 0x0000000100003f62 "@\"NSString\""
    (lldb) p $5.get(2).name
    (const char *) $10 = 0x0000000100003eff "_cjl_name"
    (lldb) p $5.get(2).type
    (const char *) $11 = 0x0000000100003f62 "@\"NSString\""
    (lldb) p $5.get(3).name
    (const char *) $12 = 0x0000000100003f09 "_number"
    (lldb) p $5.get(3).type
    (const char *) $13 = 0x0000000100003f62 "@\"NSString\""
    (lldb) p $5.get(4).name
    Assertion failed: (i < count), function get, file /Users/objc M1和因特尔芯片源码/objc源码/M1芯片mac/objc4-818.2/runtime/objc-runtime-new.h, line 624.
    error: Execution was interrupted, reason: signal SIGABRT.
    The process has been returned to the state before expression evaluation.
    (lldb)
    

    LLDB调试-协议protocol调试过程

    KCObjcBuild was compiled with optimization - stepping may behave oddly; variables may not be available.
    (lldb) p/x CJLPerson.class
    (Class) $0 = 0x0000000100008600 CJLPerson
    (lldb) p (class_data_bits_t *) 0x0000000100008620
    (class_data_bits_t *) $1 = 0x0000000100008620
    (lldb) p $1->data()
    (class_rw_t *) $2 = 0x0000000100957f50
    (lldb) p $2->protocols()
    (const protocol_array_t) $3 = {
      list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
         = {
          list = {
            ptr = 0x0000000100008410
          }
          arrayAndFlag = 4295001104
        }
      }
    }
    (lldb) p $3.list
    (const RawPtr<protocol_list_t>) $4 = {
      ptr = 0x0000000100008410
    }
    (lldb) p $4.ptr
    (protocol_list_t *const) $5 = 0x0000000100008410
    (lldb) p *$5
    (protocol_list_t) $6 = (count = 1, list = protocol_ref_t [] @ 0x000000012921a7c8)
    (lldb) p $6.list[0]
    (protocol_ref_t) $7 = 4295001744
    (lldb) p (protocol_t *)$7
    (protocol_t *) $8 = 0x0000000100008690
    (lldb) p *$8
    (protocol_t) $9 = {
      objc_object = {
        isa = {
          bits = 4298608840
          cls = Protocol
           = {
            nonpointer = 0
            has_assoc = 0
            has_cxx_dtor = 0
            shiftcls = 537326105
            magic = 0
            weakly_referenced = 0
            unused = 0
            has_sidetable_rc = 0
            extra_rc = 0
          }
        }
      }
      mangledName = 0x0000000100003c4d "CJLPersonDelegate"
      protocols = 0x0000000100008310
      instanceMethods = 0x0000000100008328
      classMethods = 0x0000000100008348
      optionalInstanceMethods = 0x0000000100008368
      optionalClassMethods = 0x0000000100008388
      instanceProperties = nil
      size = 96
      flags = 0
      _extendedMethodTypes = 0x00000001000083a8
      _demangledName = 0x0000000000000000
      _classProperties = nil
    }
    (lldb) p/x 4298608840
    (long) $10 = 0x00000001003790c8
    (lldb) p/x 0x00000001003790c8 & 0x0000000ffffffff8ULL
    (unsigned long long) $11 = 0x00000001003790c8
    (lldb) po 0x00000001003790c8
    Protocol
    
    (lldb) p $9.instanceMethods
    (method_list_t *) $13 = 0x0000000100008328
    (lldb) p *$13
    (method_list_t) $14 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
    }
    (lldb) p $14.get(0).name()
    (SEL) $15 = "sendDataToController:"
    (lldb) p $14.get(0).types()
    (const char *) $16 = 0x0000000100003d50 "v24@0:8@16"
    (lldb) p $14.get(0).imp(1)
    (IMP) $17 = 0x0000000000000000
    (lldb) p $9.classMethods
    (method_list_t *) $18 = 0x0000000100008348
    (lldb) p *$18
    (method_list_t) $19 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
    }
    (lldb) p $19.get(0).name()
    (SEL) $20 = "sendString:"
    (lldb) p $19.get(0).types()
    (const char *) $21 = 0x0000000100003d50 "v24@0:8@16"
    (lldb) p $19.get(0).imp(1)
    (IMP) $22 = 0x0000000000000000
    (lldb) p $9.optionalInstanceMethods
    (method_list_t *) $23 = 0x0000000100008368
    (lldb) p *$23
    (method_list_t) $24 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
    }
    (lldb) p $24.get(0).name()
    (SEL) $25 = "showKeyboard"
    (lldb) p $24.get(0).types()
    (const char *) $26 = 0x0000000100003d48 "v16@0:8"
    (lldb) p $24.get(0).imp(1)
    (IMP) $27 = 0x0000000000000000
    (lldb) p $9.optionalClassMethods
    (method_list_t *) $28 = 0x0000000100008388
    (lldb) p *$28
    (method_list_t) $29 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 24, count = 1)
    }
    (lldb) p $29.get(0).name()
    (SEL) $30 = "closeKeyboard"
    (lldb) p $29.get(0).types()
    (const char *) $31 = 0x0000000100003d48 "v16@0:8"
    (lldb) p $29.get(0).imp(1)
    (IMP) $32 = 0x0000000000000000
    (lldb)
    

    bits存储类信息的共同点
    无论方法、属性、协议、成员变量都放在了对应结构体的数组里,method_list_tproperty_list_tivar_list_tprotocol_list_t,说明这些是有序而且是值为真

    LLDB调试获取xxxx_list_t里面的元素方式是p $5.get(0) p $5.get(1) ... p $5.get(N)get(index)下标取法(圆括号+下标

    那么接下来objc_class里面非常重要的cache是不是也是这样的存储方式呢?

    (method_list_t) $6 = {
      entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 2147483660, count = 5)
    }
    
    (property_list_t) $6 = {
      entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
    }
    
    (const ivar_list_t) $5 = {
      entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
    }
    
    (protocol_list_t) $6 = (count = 1, list = protocol_ref_t [] @ 0x000000012921a7c8)
    

    3.isa、superclass、bits总结

    ** objc_class、objc_objcet、Class**
    1.Classobjc_class来自于底层objc_class结构体
    2.idobjc_object对象,即对象来自于objc_object结构体
    3.objc_class : objc_objectobjc_class继承自objc_object结构体,objc_classisa继承自objc_object,所以,和对象都有Class isa指针
    4.superclass有继承关系,对象没有superclass没有继承关系

    元类
    1.我们都知道 对象isa 是指向其实也是一个对象,可以称为类对象,其isa指向苹果定义的元类
    2.元类系统给的,其定义创建都是由编译器完成,在这个过程中,的归属来自于元类
    3.元类 是类对象 的类,每个都有一个独一无二的元类用来存储 类方法的相关信息。
    元类本身是没有名称的,由于与相关联,所以使用了同名一样的名称,但是本质和底层编译的名称是不一样的
    4.CJLPerson不是CJLPerson_metaClass(CJLPerson元类),内存地址不同,但是每一个只有一份
    5.NSobject不是NSobject_metaClass(根元类),内存地址不同,但是每一个只有一份

    ** isa**
    对象 ---> ---> 元类 ---> NSobject(根元类) ---> NSobject(根元类)

    superclass
    1.只有类有继承关系,对象没有继承关系
    子类 ---> 父类 ---> ...---> NSObject ---> nil
    子元类 --->父元类 ---> ...---> NSObject(根元类) ---> NSObject ---> nil

    bits
    1.实例方法里面,类方法元类里面,类方法相当于元类实例方法,由于只有实例方法类方法,所以,根元类里面是没有方法列表
    2.成员变量列表里面有属性属性列表里面没有成员变量
    3.实例方法列表包含属性settergetter方法
    4.实例对象来自于objc_object 结构体
    5.实例方法类方法协议属性成员变量都存在对应或者元类objc_class --->class_data_bits_t bits成员变量里面
    6.协议是一个对象继承自objc_object 结构体,协议里面通过各种方法列表存储协议方法

    问题
    1.Class到底是什么?Class之间的关系?
    Class就是,也是类的首地址-指针,存储内容是类名,类似于数组名和数组首地址

    2.对象isaisa之间的区别?分别存储的是什么值?
    类的isa 64位中只在对应的33位存储类的首地址,对象的isa 64位中除了类信息还有其他信息,nonpointer = 0的话,对象isa只存储类信息,但是多数对象是nonpointer = 1的

    3.对象方法存储在中,那类方法存储在哪里呢?如果一直有相应的来存储那么尽头在哪里呢?
    对象方法存储在中,类方法存储在元类中,由于只有类方法类方法,所以,根元类里面是没有方法列表的

    4.isasuperclass是什么关系? isa到底有哪些作用?
    isasuperclass都是Class类型的64位指针,都是存储类的指针,没有直接的关系,isa偏向于数据的操作逻辑属于谁,即方法存储在哪里,找到后去操作数据,superclass偏向于父类在哪里,即更高级的操作逻辑属于谁,superclass是帮助isa在不同层级的父类中查找操作逻辑

    5.在内存中到底存在几份
    在内存中只有一份,元类虽然看着名字一样,但是本质是两个独立的类,内存空间底层编译都是不同的

    相关文章

      网友评论

          本文标题:iOS-底层原理-类&类结构分析 isa--superclass

          本文链接:https://www.haomeiwen.com/subject/oamsurtx.html