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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容