RyukieDev
  • 关于我-AboutMe
  • 我的作品-MyApps
    • 「梦见」账本
      • 「梦见」账本(Umemi):极致的记账体验
      • 隐私协议:Privacy Policy
      • 服务协议:Terms of use
      • 外观预览:Preview
        • 赛博朋克-Cyberpunk
        • 樱-Sakura
        • 初恋-FirstLove
        • 永生-Eternal
        • 菲尼克斯-Phoenix
        • 报丧女妖-Banshee
        • 九霄-NYXL
        • Dream
        • 猕猴桃-Kiwi
        • 蜂蜜-Hachimitsu
        • DC
    • Elic-扫雷无尽天梯
    • 隐私访问记录
      • 03.如何分析iOS15隐私访问记录
      • PrivacyPolicy
      • FrameWorks
    • 醒词
      • PrivacyPolicy
      • TermsOfUse
    • 一色
      • PrivacyPolicy
    • 醒诗
      • PrivacyPolicy
    • 醒词键盘
      • PrivacyPolicy
    • 中文数字键盘
      • PrivacyPolicy
  • 独立开发
    • 产运
      • 01.没钱推广?这忘了这100美元
      • 02.在个人站点配置app-ads
      • 03.应用图标圆角
      • 04.iOS独立开发者注册公司到App备案上架.md
    • iCloud
      • 01.基于iCloud构建用户体系
      • 02.基于iCloud构建游戏内排行榜
  • Swift
    • 01.纯Swift路由方案探究
    • 02.使用Carthage替代CocoaPods
    • 03.逃逸闭包和非逃逸闭包
    • 04.向下向上取整
    • 05.Copy-on-write
    • 06.OC老项目Swift混编的一些坑
    • 07.OC项目中加入Swift混编
    • 08.Optional实质
    • 09.R-Swift-安全的资源组织方案forSwift
    • 10.Struct与Class
    • 11.Swift5新特性
    • 12.Swift性能分析
    • 13.SwiftPackage使用
    • 14.String与Substring
    • 15.Array,Set,Dictionary
    • 16.For-in跳跃遍历
    • 17.Switch元祖
    • 18.循环的标签控制
    • 19.Protocol与静态派发、动态派发
    • 20.Swift位移枚举
    • 21.Swift轻量级网络封装:SwiftyServiceProtocol(适用于混编或纯Swift项目)
    • 22.open与public
    • 23.Swift项目编译速度优化
    • 24.[译]编写高性能Swift代码-Writing High-Performance Swift Code(2022.8.25版)
    • 25.Swift编译流程
    • 26.Swift方法调度
  • SwiftUI
    • 01.Form
    • 02.Navigation
    • 03.ViewBuilder参数10个限制
    • 04.UIKit混编时Dismiss掉HostController
    • 05.如何在SwiftUI中使用ImagePicker?
    • 06.从some看Swift不透明类型
    • 07.TabView使用
    • 08.openURL
    • 09.Search
    • 10.SwifUI中使用WKWebView
  • DeepLearning
    • 基础知识
      • 01.感知机与神经网络
      • 02.线性可分
    • TensorFlow
      • 01.Anaconda
      • 02.JupyterNotebook
      • 03.安装TensorFlow
  • iOS
    • 底层
      • 01.alloc与字节对齐
      • 02.结构体内存对齐
      • 03.对象本质探究与isa
      • 04.ISA与Class
      • 05.深入探索Class的结构
      • 06.WWDC20-runtime优化
      • 07.深入探究属性
      • 08.isKindOfClass的底层实现
      • 09.slowpath和fastpath
      • 10.Class-cache_t
      • 11.源码解读objc_msgSend
      • 12.类的实现与初始化源码解读
      • 13.动态决议与消息转发
      • 14.iOS应用启动(一):dyld与main函数
      • 15.iOS应用启动(二):环境配置与runtime初始化
      • 16.iOS应用启动(三):镜像文件的读取和加载
      • 17.iOS应用启动(四):分类的加载
      • 18.关联对象源码解析
      • 19.MethodSwizzing方法交换的坑
      • 20.详解KVC
      • 21.KVO几个被忽视的细节
      • 22.KVO底层原理
      • 23.多线程原理与atomic
      • 24.任务与队列的几个面试题
      • 25.dispatch_once单例实现原理
      • 26.栅栏函数
      • 27.信号量
      • 28.锁|性能分析
      • 29.锁|@synchronized
      • 30.锁|递归锁
      • 31.锁|NSConditionLock
      • 32.关于Block你所该知道的一切
    • 内存管理
      • 01.从一个面试题看TaggedPointer
      • 02.Retain&Release
      • 03.SideTable和weak底层实现
      • 04.Timer优化
      • 05.自动释放池与Runloop
      • 06.dealloc
    • 编译器
      • 01.LLVM
    • 杂项
      • 01.堆栈的深度问题
      • 02.使用TTF字体
      • 03.为什么选VIPER
      • 04.项目路由方案
      • 05.隐藏导航栏下面的线
      • 06.源代码到IPA
      • 07.iOS重签名调研
      • 08.load与-initialize
      • 09.NSTimer与GCD
      • 10.NSURLConnection-和-NSURLSession
      • 11.Storyboard中UnwindSegue的使用
      • 12.UI调试-UIDebuggingInformationOverlay
      • 13.UIWebView和WKWebView
      • 14.UIWebView自适应高度
      • 15.weak实现原理
    • Runloop
      • 01.RunLoop
      • 02.autoreleasepool
    • Runtime
      • 01.基本操作
      • 02.实现NSCoding的自动归档和自动解档
      • 03.消息机制
      • 04.重写description打印对象信息
      • 05.MethodSwizzling的问题
    • 优化
      • 01.Apple官方资源瘦身方案ODR(一):初见
      • 02.Apple官方资源瘦身方案ODR(二):践行|换肤系统改造
      • 03.二进制重排实践
      • 04.iOS截屏防护方案
      • 05.提高编译速度
      • 06.图片格式-WebP
      • 07.App启动速度优化
      • 08.IDL自动化埋点
      • 09.渲染原理及优化
      • 10.「利用 Metrics 和 Diagnostics 提高性能」网络研讨活动
      • 11.离屏渲染
      • 12.一键搞定iOS16横竖屏切换
    • 多线程
      • 01.合适的线程数量
      • 02.死锁
      • 03.为什么用dispatch-once实现单例
      • 04.iOS多线程方案
      • 05.iOS多线程技术对比
    • Database
      • 01.数据库主键和外键
      • 02.FMDB-死锁问题
      • 03.FMDB与WCDB
      • 04.SQLite数据库修复
    • 架构
      • 01.组件化
  • 逆向
    • 01.寄存器
    • 03.iOS应用签名原理
    • 04.利用Xcode进行重签名与调试
    • 05.dylib注入
    • 06.MachO文件
    • 07.dyld
    • 08.Hook
    • 09.深入理解动态库与静态库
    • 10.通过符号表找到符号
    • 11.fishhook原理
    • 12.去符号与恢复符号
    • 13.反HOOK防护(一):基于Fishhook
    • 14.反HOOK防护(二):Monkey
    • 15.Inlinehook:Dobby
    • 16.LLDB
    • 17.虚拟内存
    • 18.Chisel工具
    • 19.DS.LLDB工具
    • 20.Cycript工具
    • 21.Cycrupt用法
    • 22.Logos
    • 23.应用砸壳
    • 24.实战人人视频破解
    • 25.解密被加密的数据库文件
  • Flutter
    • 01.初见Flutter
    • 02.Layout
    • 03.状态管理
    • 04.BottomNavigationBar
    • 05.MaterialApp
    • 06.android资源配置
    • 07.Positioned与Container嵌套无法充满容器
    • 08.Cell点击跳转
    • 09.代码规范
    • 10.通过联系人Cell看断言
    • 11.有状态Widget初始化重写&链式调用&排序
    • 12.索引条:手势及clamp函数
    • 13.ListView滑动到指定位置
    • 14.悬浮菜单列表
    • 15.Mock数据小技巧
    • 16.第三方库导入与网络数据异步请求与展示
    • 17.请求数据保留
    • 18.异步编程之Future
    • 19.Future&Microtask
    • 20.Dart异步编程:Isolates和事件循环
    • 21.Widget的生命周期
    • 22.Widget树&Render树&Element树
    • 23.Key
    • 24.调用原生相册
    • 25.iOS原生嵌入FlutterModule
  • 网络
    • 01 网络分层的优点
    • 02 网络理解
    • 03 iOS-网络安全之HTTPS
    • 04 POST和GET
    • 05 SSL-TLS四次握手
  • 直播技术
    • 01 直播技术相关
    • Socket-Little-Endian-Big-Endian
  • 知识点梳理
    • 01 面试算法题记录01
    • 02 面试题记录-C语言
    • 08 一套iOS底层试卷
    • 03 知识点梳理:iOS底层
    • 04 知识点梳理:网络
    • 05 知识点梳理:多线程
    • 06 知识点梳理:计算机基础
    • 07 知识点梳理:算法数据结构
    • 09 知识点梳理:HTML和浏览器
    • 10 知识点梳理:JavaSctipt
  • Framework
    • 01 CodeReading-01-YYModel
    • 02 RYImagePicker-iOS图片视频选择器
    • 03 RYImagesScroller-iOS高度自定义的图片轮播器
    • 04 RYPhotosBrowser
  • Issue
    • 01 使用KVC设置UIAlertAction按钮颜色的Crash
    • 02 iOS-常见崩溃分析
    • 03 UICollectionView的一些问题
  • OpenGL ES
    • 01.顶点着色器与片元着色器
  • 数据结构与算法
    • 剑指Offer-Swift
      • 03.找出数组中重复的数字
      • 04.二维数组中的查找
      • 05.替换空格
      • 06.从尾到头打印链表
      • 07.重建二叉树
      • 12.矩阵中的路径(回溯法)
      • 13.机器人的运动范围
      • 14.I.剪绳子
      • 14.II.剪绳子
      • 15.二进制中1的个数(含一个拓展问题)
      • 16.数值的整数次方
      • 18.删除链表的节点
      • 21.调整数组顺序使奇数位于偶数前面
      • 22.链表中倒数第k个节点
      • 24.反转链表
      • 25.合并两个排序的链表
      • 26.树的子结构
      • 27.二叉树的镜像
      • 28.对称的二叉树
      • 29.顺时针打印矩阵
      • 30.包含min函数的栈(容易被误导的一题)
      • 31.栈的压入、弹出序列
      • 32.I.从上到下打印二叉树
      • 32.II.从上到下打印二叉树II
      • 32.III.从上到下打印二叉树III
      • 32.从上到下花式打印二叉树
      • 33.二叉搜索树的后序遍历序列
      • 34.二叉树中和为某一值的路径
      • 35.复杂链表的复制(无Swift用例)
      • 36.二叉搜索树与双向链表
      • 37.序列化二叉树
      • 39.数组中出现次数超过一半的数字
      • 40.最小的k个数
      • 41.数据流中的中位数
      • 42.连续子数组的最大和
      • 43.1~n整数中1出现的次数
      • 44.数字序列中某一位的数字
      • 45.把数组排成最小的数
      • 46.把数字翻译成字符串
      • 47.礼物的最大价值
      • 48.最长不含重复字符的子字符串
      • 50.第一个只出现一次的字符
      • 52.两个链表的第一个公共节点
      • 53-I.在排序数组中查找数字
      • 53-II.0~n-1中缺失的数字
      • 54.二叉搜索树的第k大节点
      • 55-I.二叉树的深度
      • 55-II.平衡二叉树
      • 56-I.数组中数字出现的次数
      • 56-II.数组中数字出现的次数II
      • 57.和为s的两个数字
      • 58-I.翻转单词顺序
      • 58-II.左旋转字符串
      • 59-I.滑动窗口的最大值
      • 59-II.队列的最大值
      • 60.n个骰子的点数
      • 61.扑克牌中的顺子
      • 62.圆圈中最后剩下的数字
      • 63.股票的最大利润
      • 64.求1+2+…+n
      • 65.不用加减乘除做加法
      • 66.构建乘积数组
      • 67.把字符串转换成整数
      • 68-I.二叉搜索树的最近公共祖先
      • 68-II.二叉树的最近公共祖先
    • 技巧
      • 01.前缀和
      • 02.同余性质
      • 03.快速幂
      • 04.快速排序
      • 05.深度优先&广度优先
      • 06.冒泡排序
      • 07.摩尔投票
      • 08.优先队列
    • 数据结构
Powered by GitBook
On this page
  • 一、内存平移
  • 二、Class的结构内存计算
  • 三、objc_class: cache_t 的内存结构
  • 3.1 小补充:LP64数据模型
  • 3.2 内部联合体的内存结构
  • 3.3 结构总结
  • 四、objc_class : class_data_bits_t 的内存结构
  • 五、class_data_bits_t : class_rw_t
  • 5.1 class_rw_t的内存结构
  • 5.2 一些重要函数
  • 六、class_data_bits_t : class_rw_t : class_rw_ext_t
  • 6.1 list_array_tt
  • 6.2 class_ro_t
  • 七、LLDB调试Class内存结构
  • 结构图
  • 7.1 成员变量
  • 7.2 实例方法列表
  • 7.3 协议列表
  • 7.4 成员变量
  • 7.5 类方法

Was this helpful?

  1. iOS
  2. 底层

05.深入探索Class的结构

一、内存平移

我们先通过下面的例子了解一下内存平移

int nums[4] = {1, 2, 3, 4};

NSLog(@"数组nums的地址:%p\n", &nums);

for (int i = 0; i < 4; i++) {
    NSLog(@"第%d个元素是%d地址是%p", i + 1, nums[i], &nums[i]);
}

NSLog(@"\n");

int *numsPointer = nums;
NSLog(@"数组nums的指针:%p\n", numsPointer);

for (int i = 0; i < 4; i++) {
    NSLog(@"偏移%d,取值%d,地址是%p", i, *(numsPointer + i), (numsPointer + i));
}

输出:

2021-06-22 23:27:02.344397+0800 Class[15488:5517067] 数组nums的地址:0x16f00d3a0

2021-06-22 23:27:02.344734+0800 Class[15488:5517067] 第1个元素是11地址是0x16f00d3a0
2021-06-22 23:27:02.344910+0800 Class[15488:5517067] 第2个元素是12地址是0x16f00d3a4
2021-06-22 23:27:02.345776+0800 Class[15488:5517067] 第3个元素是13地址是0x16f00d3a8
2021-06-22 23:27:02.346025+0800 Class[15488:5517067] 第4个元素是14地址是0x16f00d3ac
2021-06-22 23:27:02.346173+0800 Class[15488:5517067] 
2021-06-22 23:27:02.346328+0800 Class[15488:5517067] 数组nums的指针:0x16f00d3a0

2021-06-22 23:27:02.347933+0800 Class[15488:5517067] 偏移0,取值11,地址是0x16f00d3a0
2021-06-22 23:27:02.348115+0800 Class[15488:5517067] 偏移1,取值12,地址是0x16f00d3a4 // 4字节
2021-06-22 23:27:02.348425+0800 Class[15488:5517067] 偏移2,取值13,地址是0x16f00d3a8 // 4字节
2021-06-22 23:27:02.349788+0800 Class[15488:5517067] 偏移3,取值14,地址是0x16f00d3ac // 4字节

nums一个元素4字节,指针偏移1次4字节。所以这里可以通过从数组首地址开始偏移的方式进行取值操作。

同理对Class的内存结构我们是否也能进行内存平移来取值呢?

二、Class的结构内存计算

在Class的源码中我们发现核心的数据结构是这样的,暂时忽略其中的方法,因为不影响Class的内存结构。

方法都存在方法区

struct objc_class : objc_object {
    ...

    // Class ISA; objc_object 中的 ISA
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    ...
}

-

ISA

superClass

cache

bits

说明

ISA(结构体指针)

父类(结构体指针)

方法缓存

类的具体信息

大小(字节)

8

8

16

8

三、objc_class: cache_t 的内存结构

我们忽略掉方法,剩下的核心数据结构如下

struct cache_t {
private:
    explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
    union {
        struct {
            explicit_atomic<mask_t>    _maybeMask;
#if __LP64__
            uint16_t                   _flags;
#endif
            uint16_t                   _occupied;
        };
        explicit_atomic<preopt_cache_t *> _originalPreoptCache;
    };
}

3.1 小补充:LP64数据模型

在里面我们可以看到 __LP64__,这里代表的是LP64数据模型。现今所有64位的类Unix平台均使用LP64数据模型。

TYPE

LP32

ILP32

LP64

ILP64

LLP64

含义

指long和pointer是32位的

指int,long和pointer是32位的

指long和pointer是64位

指int,long,pointer是64位

指long long和pointer是64位的

CHAR

8

8

8

8

8

SHORT

16

16

16

16

16

INT

16

32

32

64

32

LONG

32

32

64

64

32

LONG LONG

64

64

64

64

64

POINTER

32

32

64

64

64

3.2 内部联合体的内存结构

联合体包含了两部分:结构体、指针。我们已知arm64下指针占用8字节。我们来看下结构体需要占用多少

-

_maybeMask

_flags

类型

uint32_t

uint16_t

大小(字节)

4

2

结构体大小 8 字节,联合体大小为8字节

3.3 结构总结

-

_bucketsAndMaybeMask

union

说明

指针(做什么的还需要研究)

联合体

大小(字节)

long类型 8

8

cache_t大小为16字节

四、objc_class : class_data_bits_t 的内存结构

核心数据结构如下

struct class_data_bits_t {
  ...
  // Values are the FAST_ flags above.
  uintptr_t bits;
  ...
}

我们发现内部只有一个指针bits,那么如何通过它获取到类的详细信息呢?

我们发现这样一个函数,通过bits与上FAST_DATA_MASK可以获得class_rw_t。和我们找ISA的过程比较类似。

那么它是做什么的呢?

class_rw_t* data() const {
    return (class_rw_t *)(bits & FAST_DATA_MASK);
}

FAST_DATA_MASK在LP64下的定义

#if __LP64__
...
// data pointer
#define FAST_DATA_MASK          0x00007ffffffffff8UL

五、class_data_bits_t : class_rw_t

rw: read-write

5.1 class_rw_t的内存结构

struct class_rw_t {
  // Be warned that Symbolication knows the layout of this structure.
  // 标志位,如是否是元类,是否实现了等
  uint32_t flags;
  uint16_t witness;

  explicit_atomic<uintptr_t> ro_or_rw_ext; //

  Class firstSubclass;
  Class nextSiblingClass;

}

5.2 一些重要函数

这里我们会看到一些眼熟的函数,在methods() properties() protocols()中我们发现了另一个重要的类型class_rw_ext_t

class_rw_ext_t *deepCopy(const class_ro_t *ro) {
    return extAlloc(ro, true);
}

const method_array_t methods() 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)->methods;
    } else {
        return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
    }
}

const property_array_t properties() const {
    ...
}

const protocol_array_t protocols() const {
    ...
}

六、class_data_bits_t : class_rw_t : class_rw_ext_t

struct class_rw_ext_t {
    DECLARE_AUTHED_PTR_TEMPLATE(class_ro_t)
    class_ro_t_authed_ptr<const class_ro_t> ro;// 成员变量
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    char *demangledName;
    uint32_t version;
};

6.1 list_array_tt

method_array_t/property_array_t/protocol_array_t 都是继承于 list_array_tt 的,通过泛型来完成不同的定义。

源码核心部分:

template <typename Element, typename List, template<typename> class Ptr>
class list_array_tt {
  ...
  private:
    union {
        Ptr<List> list;
        uintptr_t arrayAndFlag;
    };
  ...
}
  1. method_array_t

class method_array_t : 
    public list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> {...}
  1. property_array_t

class property_array_t : 
    public list_array_tt<property_t, property_list_t, RawPtr> {...}
  1. protocol_array_t

class protocol_array_t : 
    public list_array_tt<protocol_ref_t, protocol_list_t, RawPtr> {...}

6.2 class_ro_t

源码核心部分:

struct class_ro_t {
  ...
  const ivar_list_t * ivars; // 成员变量
  ...
}

七、LLDB调试Class内存结构

结构图

有这样的一个类

@interface RYModel : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, assign) float height;
@property (nonatomic, strong) RYModel *father;
@property (nonatomic, assign) BOOL isBoy;

- (void)dosomething;

- (void)dosomethingWith:(NSString *)title;

+ (void)classDoSomething;

- (NSString *)sayMyName;

@end

7.1 成员变量

获取class_data_bits_t

这里通过内存偏移的方式找到class_data_bits_t,如前文所述:bits所在位置的偏移为8+8+16=32字节。

那么我们理论上可以通过对象地址+0x20计算出它的位置。

(lldb) p/x RYModel.class
(Class) $0 = 0x00000001000083e0 RYModel

(lldb) x/4gx 0x00000001000083e0
0x1000083e0: 0x00000001000083b8 0x000000010036a140
0x1000083f0: 0x000000010073c2d0 0x0004803400000007

// 计算bits
(lldb) p/x 0x1000083e0 + 0x20 
(long) $1 = 0x0000000100008400

// 这里转换一下类型便于后面继续调试
(lldb) p (class_data_bits_t *)0x0000000100008400 
(class_data_bits_t *) $2 = 0x0000000100008400

通过 data() 方法获取 class_rw_t

(lldb) p (class_data_bits_t *)0x0000000100008400
(class_data_bits_t *) $4 = 0x0000000100008400

(lldb) p $4->data()
(class_rw_t *) $5 = 0x000000010073c040

通过 properties 获取属性列表容器

(lldb) p $5->properties
(const property_array_t) $6 = {
  list_array_tt<property_t, property_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x0000000100008280
      }
      arrayAndFlag = 4295000704
    }
  }
}
  Fix-it applied, fixed expression was: 
    $5->properties()

取出 list

(lldb) p $6.list
(const RawPtr<property_list_t>) $7 = {
  ptr = 0x0000000100008280
}

取出 ptr

(lldb) p $7.ptr
(property_list_t *const) $8 = 0x0000000100008280

取出 ptr 内的值

(lldb) p *$8
(property_list_t) $9 = {
  entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 5)
}

通过下标取出属性

(lldb) p $9.get(0)
(property_t) $10 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $9.get(1)
(property_t) $11 = (name = "age", attributes = "Tq,N,V_age")
(lldb) p $9.get(2)
(property_t) $12 = (name = "height", attributes = "Tf,N,V_height")
(lldb) p $9.get(3)
(property_t) $13 = (name = "father", attributes = "T@\"RYModel\",&,N,V_father")
(lldb) p $9.get(4)
(property_t) $14 = (name = "isBoy", attributes = "Tc,N,V_isBoy")

7.2 实例方法列表

前面一些相似的步骤我们省略掉

从 class_rw_t -> methods 获取 方法列表容器

(lldb) p $5->methods
(const method_array_t) $6 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x00000001000080e8
      }
      arrayAndFlag = 4295000296
    }
  }
}
  Fix-it applied, fixed expression was: 
    $5->methods()

取出 list

(lldb) p $6.list
(const method_list_t_authed_ptr<method_list_t>) $14 = {
  ptr = 0x00000001000080e8
}

取出 ptr

(lldb) p $14.ptr
(method_list_t *const) $15 = 0x00000001000080e8

取出 ptr 中的数据 method_list_t

(lldb) p *$15
(method_list_t) $16 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 14)
}

获取方法数量

(lldb) p $16.count
(uint32_t) $29 = 14

通过c++函数get()与big()单个获取类的实例方法

这里除了我们自己定义的一些实例方法,还看到了一些列属性自动生成的set、get方法,以及析构函数

(lldb) p $16.get(0).big()
(method_t::big) $19 = {
  name = "setFather:"
  types = 0x0000000100003f60 "v24@0:8@16"
  imp = 0x0000000100003cc0 (ObjcBuild`-[RYModel setFather:])
}
(lldb) p $16.get(1).big()
(method_t::big) $20 = {
  name = "father"
  types = 0x0000000100003f6b "@16@0:8"
  imp = 0x0000000100003ca0 (ObjcBuild`-[RYModel father])
}

... // 省略一些Set、get

(lldb) p $16.get(3).big()
(method_t::big) $22 = {
  name = "dosomething"
  types = 0x0000000100003f3b "v16@0:8"
  imp = 0x0000000100003b30 (ObjcBuild`-[RYModel dosomething])
}
(lldb) p $16.get(4).big()
(method_t::big) $23 = {
  name = "dosomethingWith:"
  types = 0x0000000100003f60 "v24@0:8@16"
  imp = 0x0000000100003b40 (ObjcBuild`-[RYModel dosomethingWith:])
}

...

(lldb) p $16.get(8).big()
(method_t::big) $27 = {
  name = ".cxx_destruct"
  types = 0x0000000100003f3b "v16@0:8"
  imp = 0x0000000100003d30 (ObjcBuild`-[RYModel .cxx_destruct])
}

... // 省略一些Set、get

7.3 协议列表

前面一些相似的步骤我们省略掉

从 class_rw_t -> protocols 获取 协议列表容器

(lldb) p $2->protocols()
(const protocol_array_t) $3 = {
  list_array_tt<unsigned long, protocol_list_t, RawPtr> = {
     = {
      list = {
        ptr = 0x00000001000083b8
      }
      arrayAndFlag = 4295001016
    }
  }
}

取出 list

(lldb) p $3.list
(const RawPtr<protocol_list_t>) $4 = {
  ptr = 0x00000001000083b8
}

取出 ptr

(lldb) p $4.ptr
(protocol_list_t *const) $5 = 0x00000001000083b8

取出 ptr 中的数据 protocol_list_t

(lldb) p *$5
(protocol_list_t) $6 = (count = 1, list = protocol_ref_t [] @ 0x00007f9a62cb8778)

这里可以发现,里面的list中存了一个指针,并不是完整的一个数据结构。我们找到了这样的定义:

typedef uintptr_t protocol_ref_t; // protocol_t *, but unremapped

获取 protocol_t *

(lldb) p (protocol_t *)$6.list[0]
(protocol_t *) $7 = 0x00000001000088a0

读取数据

(lldb) p *$7
(protocol_t) $8 = {
  objc_object = {
    isa = {
      bits = 4298547400
      cls = Protocol
       = {
        nonpointer = 0
        has_assoc = 0
        has_cxx_dtor = 0
        shiftcls = 537318425
        magic = 0
        weakly_referenced = 0
        unused = 0
        has_sidetable_rc = 0
        extra_rc = 0
      }
    }
  }
  mangledName = 0x0000000100003e71 "RYProtocol"
  protocols = 0x0000000100008310
  instanceMethods = 0x0000000100008328
  classMethods = nil
  optionalInstanceMethods = nil
  optionalClassMethods = nil
  instanceProperties = nil
  size = 96
  flags = 0
  _extendedMethodTypes = 0x0000000100008348
  _demangledName = 0x0000000000000000
  _classProperties = nil
}

7.4 成员变量

前面一些相似的步骤我们省略掉

从 class_rw_t -> ro() 获取 成员变量列表容器

(lldb) p $4->ro()
(const class_ro_t *) $18 = 0x00000001000083d0

class_ro_t -> ivars 获取成员变量列表

(lldb) p $18->ivars
(const ivar_list_t *const) $19 = 0x0000000100008570

(lldb) p *$19
(const ivar_list_t) $20 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 5)
}

从成员变量列表中获取成员变量

(lldb) p $20.count
(const uint32_t) $21 = 5

(lldb) p $20.get(0)
(ivar_t) $22 = {
  offset = 0x0000000100008770
  name = 0x0000000100003de7 "_isBoy"
  type = 0x0000000100003f47 "c"
  alignment_raw = 0
  size = 1
}
(lldb) p $20.get(1)
(ivar_t) $23 = {
  offset = 0x0000000100008778
  name = 0x0000000100003dee "_height"
  type = 0x0000000100003f49 "f"
  alignment_raw = 2
  size = 4
}
(lldb) p $20.get(2)
(ivar_t) $24 = {
  offset = 0x0000000100008780
  name = 0x0000000100003df6 "_name"
  type = 0x0000000100003f4b "@\"NSString\""
  alignment_raw = 3
  size = 8
}
(lldb) p $20.get(3)
(ivar_t) $25 = {
  offset = 0x0000000100008788
  name = 0x0000000100003dfc "_age"
  type = 0x0000000100003f57 "q"
  alignment_raw = 3
  size = 8
}
(lldb) p $20.get(4)
(ivar_t) $26 = {
  offset = 0x0000000100008790
  name = 0x0000000100003e01 "_father"
  type = 0x0000000100003f59 "@\"RYModel\""
  alignment_raw = 3
  size = 8
}

7.5 类方法

类方法我们需要到元类中进行查找

获取元类类信息

(lldb) p/x RYModel.class
(Class) $67 = 0x00000001000087c0 RYModel
(lldb) x/4gx 0x00000001000087c0
0x1000087c0: 0x0000000100008798 0x000000010036a140
0x1000087d0: 0x00000001010695c0 0x0004803400000007
(lldb) p/x 0x0000000100008798 & 0x00007ffffffffff8ULL
(unsigned long long) $68 = 0x0000000100008798
(lldb) p 0x0000000100008798 + 0x20
(long) $70 = 4295002040
(lldb) p/x 0x0000000100008798 + 0x20
(long) $71 = 0x00000001000087b8
(lldb) p (class_data_bits_t *)0x00000001000087b8
(class_data_bits_t *) $72 = 0x00000001000087b8

(lldb) p $72->data()
(class_rw_t *) $73 = 0x0000000101069510
(lldb) p $73->methods()
(const method_array_t) $74 = {
  list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
     = {
      list = {
        ptr = 0x0000000100008398
      }
      arrayAndFlag = 4295000984
    }
  }
}

获取元类方法列表(类方法)

(lldb) p $74.list
(const method_list_t_authed_ptr<method_list_t>) $75 = {
  ptr = 0x0000000100008398
}
(lldb) p $75.ptr
(method_list_t *const) $76 = 0x0000000100008398
(lldb) p *$76
(method_list_t) $77 = {
  entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}

输出方法信息

(lldb) p $77.get(0).big()
(method_t::big) $78 = {
  name = "classDoSomething"
  types = 0x0000000100003f3f "v16@0:8"
  imp = 0x0000000100003920 (ObjcBuild`+[RYModel classDoSomething])
}
Previous04.ISA与ClassNext06.WWDC20-runtime优化

Last updated 3 years ago

Was this helpful?

内存平移
Class的内存结构