05-Class的结构以及class_rw_t和class_ro_t的区别

简介: 05-Class的结构以及class_rw_t和class_ro_t的区别

isa地址指向验证

全文围绕下面这样一段代码展开分析:

@protocol PersonProtocal <NSObject>
@optional
- (void)protocalInstanceMethod;
+ (void)protocalClassMethod;
@end
@interface Person : NSObject<PersonProtocal> {
    int _age;
}
@property(assign, nonatomic) int height;
- (void)instanceMethod;
+ (void)classMethod;
@end
@implementation Person
- (void)instanceMethod {}
+ (void)classMethod {}
@end
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        Class personCls = [person class];
        Class personMetaClass = object_getClass(personCls);
        NSLog(@"%p - %p - %p", person, personCls, personMetaClass);
    }
    return 0;
}

上文(03-isa指针 & superclass指针)我们分析到实例对象的isa指向类对象,接下来我们调试一下:

(lldb) p/x (long)person->isa
(long) $0 = 0x011d800100008579
(lldb) p/x personCls
(Class) $1 = 0x0000000100008578 Person
(lldb)

我们会发现person的isa地址和类对象的地址并不相同,具体是什么原因产生的?我们直接分析objc源码,我们会看到下面这样一段获取真实的isa,真实的isa要对象的地址需要&上ISA_MASK的值,才是真正的isa的值;

ISA_MASK的值:

# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
#endif

再来分析一下isa的值,会发现结论和我们前文所分析的一致,直接看图:

objc_class分析

objc_class结构

首先看一下Class的类型,为objc_class结构体指针;

typedef struct objc_class *Class;

通过objc源码再来看一下objc_class结构体,发现objc_class继承自objc_object:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache; // 方法缓存
    class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
    class_rw_t *data() const {
        class_rw_t *_data = bits.data(); //(bits & FAST_DATA_MASK)
        return _data;
    }
   ...
};

再看一下objc_object,里面只有一个isa成员(其他成员已废弃、这里没有贴出来):

struct objc_class {
    Class _Nonnull isa OBJC_ISA_AVAILABILITY;
} OBJC2_UNAVAILABLE;
  • superclass:Class类型,代表指向父类的指针,占用8个字节,前文已经分析过它的作用;

  • cache_t:通过以下代码分析,我们可以知道cache_t一共占用了16个字节;

#if __LP64__
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //8个字节
    union {
        struct {
            explicit_atomic<mask_t> _maybeMask; //uint16_t 2个字节 uint32_t 4个字节
#if __LP64__
            uint16_t                   _flags; // 2个字节
#endif
            uint16_t                   _occupied; // 2个字节
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache; //8个字节
    };
    ...
}
  • bits:我们可以看到通过调用bits的data函数,我们可以得到一个class_rw_t类型的一个结构体指针,并且bits的值是通过平移上述8(isa指针)+8+16=32个字节得到;

class_rw_t

如下摘取了里面存储的一些主要信息:class_ro_t、methods(方法列表)、properties(属性列表)、protocols(协议列表);其中class_rw_t中还会存储分类里面的内容;

struct class_rw_t {
    uint32_t flags;
    uint16_t witness;
#if SUPPORT_INDEXED_ISA
    uint16_t index;
#endif
    explicit_atomic<uintptr_t> ro_or_rw_ext;
    Class firstSubclass;
    Class nextSiblingClass;
    const class_ro_t *ro();
    const method_array_t methods(); //方法列表
    const property_array_t properties(); //属性列表
    const protocol_array_t protocols(); //协议列表
};
• methods:是一个二维数组,里面放着很多个method_list_t(里面放着类本身的方法以及分类的方法),method_list_t中放着很多个method_t,method_t的结构如下:
struct method_t {
    SEL name; //函数名
    const char *types; //编码
    MethodListIMP imp; //using MethodListIMP = IMP; 函数指针
};

SEL:代表方法、函数名,底层结构跟char *类似;可以通过@selector()和sel_registerName()获得;通过sel_getName()和NSStringFromSelector()转成字符串;不同类中相同名字的方法,所对应的方法选择器是相同的;

typedefstructobjc_selector *SEL;

types包含了函数的返回值、参数的编码的字符串,可以利用@encode()返回编码该类型的字符串,具体可以参考Apple官网的映射表:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.htmlIMP:函数的具体实现:

typedefid _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);

接下来通过lldb调试分析一下实例方法的存储内容,类方法可以直接修改第一步p/x personMetaClass即可:

(lldb) p/x personCls
(Class) $0 = 0x0000000100008658 Person
(lldb) p/x 0x0000000100008658 + 0x20 //此处是因为向右平移了32个字节
(long) $1 = 0x0000000100008678
(lldb) p (class_data_bits_t *)0x0000000100008678
(class_data_bits_t *) $2 = 0x0000000100008678
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101263340
(lldb) p $3->methods()
(const method_array_t) $4 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x00000001000084e0
      }
      arrayAndFlag = 4295001312
    }
  }
}
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
  ptr = 0x00000001000084e0
}
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x00000001000084e0
(lldb) p *$6
(method_list_t) $7 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 3)
}
(lldb) p $7.get(0).big()
(method_t::big) $8 = {
  name = "instanceMethod"
  types = 0x0000000100003d2e "v16@0:8"
  imp = 0x00000001000039d0 (KCObjcBuild`-[Person instanceMethod] at main.mm:25)
}
(lldb) p $7.get(1).big() //big函数返回method_t里面存储这方法名、类型等信息
(method_t::big) $9 = {
  name = "setHeight:"
  types = 0x0000000100003d40 "v20@0:8i16"
  imp = 0x0000000100003a00 (KCObjcBuild`-[Person setHeight:] at main.mm:19)
}
(lldb) p $7.get(2).big()  
(method_t::big) $10 = {
  name = "height"
  types = 0x0000000100003d38 "i16@0:8"
  imp = 0x00000001000039e0 (KCObjcBuild`-[Person height] at main.mm:19)
}
(lldb) p $7.get(3).big()
Assertion failed: (i < count), function get, file , line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
  • protocols:存储着协议相关信息;
  • properties:可以看到里面存储着类的属性信息,但并不包括成员变量信息;调试信息如下:

(lldb) p/x personCls
(Class) $0 = 0x0000000100008660 Person
(lldb) p/x 0x0000000100008660+0x20
(long) $1 = 0x0000000100008680
(lldb) p (class_data_bits_t *)0x0000000100008680
(class_data_bits_t *) $2 = 0x0000000100008680
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010144d230
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295001240
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
(lldb) p $4.properties()
(const property_array_t) $5 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008578
      }
      arrayAndFlag = 4295001464
    }
  }
}
(lldb) p $5.list
(const RawPtr<property_list_t>) $6 = {
  ptr = 0x0000000100008578
}
(lldb) p $6.ptr
(property_list_t *const) $7 = 0x0000000100008578
(lldb) p *$7
(property_list_t) $8 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 5)
}
(lldb) p $8.get(0)
(property_t) $9 = (name = "height", attributes = "Ti,N,V_height")
(lldb) p $8.get(1)
(property_t) $10 = (name = "hash", attributes = "TQ,R")
(lldb) p $8.get(2)
(property_t) $11 = (name = "superclass", attributes = "T#,R")
(lldb) p $8.get(3)
(property_t) $12 = (name = "description", attributes = "T@\"NSString\",R,C")
(lldb) p $8.get(4)
(property_t) $13 = (name = "debugDescription", attributes = "T@\"NSString\",R,C")
(lldb) p $8.get(5)
Assertion failed: (i < count), function get, file .
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

class_ro_t

class_ro_t为class_rw_t内的一个成员,存储着编译期已经确定的协议、属性、方法、成员变量,不包含分类的信息;

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    explicit_atomic<const char *> name;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars; //成员变量列表
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    const char *getName() ;
    method_list_t *baseMethods();
};

主要看一下成员变量的存储:

(lldb) p/x personCls
(Class) $0 = 0x0000000100008660 Person
(lldb) p/x 0x0000000100008660+0x20
(long) $1 = 0x0000000100008680
(lldb) p (class_data_bits_t *)0x0000000100008680
(class_data_bits_t *) $2 = 0x0000000100008680
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010152a830
(lldb) p *$3
(class_rw_t) $4 = {
  flags = 2148007936
  witness = 1
  ro_or_rw_ext = {
    std::__1::atomic<unsigned long> = {
      Value = 4295001104
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSUUID
}
(lldb) p $4.ro()
(const class_ro_t *) $6 = 0x0000000100008410
(lldb) p *$6
(const class_ro_t) $7 = {
  flags = 128
  instanceStart = 8
  instanceSize = 16
  reserved = 0
   = {
    ivarLayout = 0x0000000000000000
    nonMetaclass = nil
  }
  name = {
    std::__1::atomic<const char *> = "Person" {
      Value = 0x0000000100003c74 "Person"
    }
  }
  baseMethodList = 0x0000000100008458
  baseProtocols = 0x00000001000083f8
  ivars = 0x00000001000084a8
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x00000001000084f0
  _swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $7.ivars
(const ivar_list_t *const) $8 = 0x00000001000084a8
(lldb) p *$8
(const ivar_list_t) $9 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 2)
}
(lldb) p $9.get(0)
(ivar_t) $10 = {
  offset = 0x0000000100008628
  name = 0x0000000100003f22 "_age"
  type = 0x0000000100003d3a "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $9.get(1)
(ivar_t) $11 = {
  offset = 0x0000000100008630
  name = 0x0000000100003f27 "_height"
  type = 0x0000000100003d3a "i"
  alignment_raw = 2
  size = 4
}
(lldb) p $9.get(2)
Assertion failed: (i < count), function get, file , line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)

总结

Class与objc_class以及objc_object的关系?

Class为为objc_class结构体指针,objc_class继承自objc_object,objc_object里面只有一个isa成员;

class_rw_t和class_ro_t的区别?

class_ro_t为class_rw_t内的一个成员,存储着编译期已经确定的类里面的协议、属性、方法、成员变量信息,不包含分类的信息;

class_rw_t里面主要存储:class_ro_t、methods(方法列表)、properties(属性列表)、protocols(协议列表)、以及分类里面的内容,不包含成员变量信息;

运行时会将class_ro_t里面内容合并到class_rw_t中(具体是在realizeClass函数中,可参考源码),之后又将分类里面的内容也合并到class_rw_t中;本文部分内容来可能来源于网络,发布的内容如果侵犯了您的权益,请联系我们尽快删除!

目录
打赏
0
0
0
0
3
分享
相关文章
iOS 常见触发离屏渲染场景及优化方案总结
iOS 常见触发离屏渲染场景及优化方案总结
1106 0
iOS 常见触发离屏渲染场景及优化方案总结
|
9月前
|
linux/mac 下查看、修改文件权限的命令
这篇文章介绍了在Linux和Mac操作系统下如何查看和修改文件及文件夹的权限。
444 0
Swift开发——元组
Swift中的元组是一种数据结构,用于组合不同类型的值。它们不是独立的数据类型,而是以有序序列形式存在,用圆括号括起,元素间用逗号分隔。元组可以有任意数量和类型的元素,可变性取决于其定义。常用于函数返回多个值。示例代码展示了元组的创建、访问、解包及赋值。元组可以通过标签来标识元素,支持嵌套和比较。在函数返回值和并行赋值场景中,元组特别有用。
187 0
Swift开发——元组
iOS 中的并发编程:GCD 与 Operation 的对比与实践
【4月更文挑战第23天】 在iOS开发中,为了提高应用的性能和响应能力,理解并合理运用并发编程是至关重要的。本文将深入探讨两种主要的并发模式:Grand Central Dispatch (GCD) 和 NSOperation。我们将比较它们的优势和局限性,并通过代码示例演示如何在实际场景中应用这两种技术来优化应用性能。文章旨在为开发者提供一个清晰的指南,以便在选择适合自己项目的并发工具时做出明智的决策。
探索iOS应用开发的新趋势
在数字时代的浪潮中,iOS应用开发领域正经历着翻天覆地的变化。从增强现实(AR)的融入,到人工智能(AI)的广泛应用,再到隐私保护的重视,这些新趋势不仅重新定义了用户体验,也为开发者带来了前所未有的挑战与机遇。本文将深入探讨这些变化如何影响着iOS应用的开发,以及开发者如何利用这些新技术创造出更加智能、安全且引人入胜的应用。
Flutter引擎工作原理:深入解析FlutterEngine
【4月更文挑战第26天】FlutterEngine是Flutter应用的关键,负责Dart代码转换为原生代码,管理应用生命周期、渲染和事件处理。它初始化Flutter运行时环境,加载并编译Dart代码,创建渲染树,处理事件并实现跨平台兼容。通过理解其工作原理,开发者能更好地掌握Flutter应用内部机制并优化开发。随着Flutter生态系统发展,FlutterEngine将持续提供强大支持。
Go语言中的中间件设计与实现
【5月更文挑战第4天】Go语言中的中间件在HTTP请求处理中扮演重要角色,提供了一种插入逻辑层的方式,便于实现日志、认证和限流等功能,而不增加核心代码复杂性。中间件遵循`http.Handler`接口,通过函数组合实现。常见问题包括错误处理(确保中间件能正确处理并传递错误)和请求上下文管理(使用`context.Context`共享数据以避免并发问题)。通过理解中间件机制和最佳实践,可以构建更健壮的Web应用。
245 0
Hash 算法详细介绍与实现 (二)
书接上回,昨天写了第一部分,《Hash 算法详细介绍与实现 (一)》详细介绍了Hash表和Hash算法的相关概念以及算法的基本原理。同时简单介绍几种hash算法的实现:直接取余法和乘法取整法;本文接着详细唠唠Hash算法和Hash表这个数据结构的具体实现以及Hash算法和Hash表常见问题的解决方案,比如解决Hash表的冲突问题等等.相关的理论知识已在上篇文章详细介绍,这里就不再赘述,多的不说少的不唠,直接进入今天的主题:利用Hash算法实现Hash表
615 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等