OC进阶 - super本质 | 内存寻址
前期准备
1 - 本篇共用的 Person 文件
内存寻址
1 - 以下代码是否会编译成功,如果编译成功会输出什么 ?
编译可以通过:我们知道 OC 方法默认自带两个参数 self、_cmd, [obj print] 其实是给 obj 发送 print 消息;也就是说实例对象调用方法是通过 isa 找到它的类对象,然后在类对象中寻找到该方法并执行。obj 的指向是 Person 类,这一点并没有改变,所以编译通过
至于会输出什么,这里先不作答!
2 - 以下代码会输出什么
输出 my name is 123
原因:① 局部变量存在栈区,而栈区内存分配方式是由高到低的;② 实例对象其实就是一个结构体,前 8 字节存放着 isa 指针,后面才是实例对象所拥有的成员变量,实例对象在获取成员变量的值时会跳过前 8 个字节;obj 毕竟不是实例对象,obj->name 在取 name 值时按照 OC 实例对象的取值规则就会跳过 isa 后开始取值!所以 obj 跳过 8 字节后取的值刚好是 test 变量的地址
测试一:代码会输出什么
打印的是 objAdded 对象,输出:my name is (null)
测试二:代码会输出什么
输出结果和 测试一 完全一样,因为 obj 指向的是 cls 地址
super 关键字
1 - 现在回过头来查看刚开始时的代码,它会输出什么
输出:my name is <ViewController: 0x600034a9ccb0>
程序 crash
2 - 为了搞明白上面两个打印结果差异的原因,我们新建 Person 的子类 BSStudent
① 将 BSStudent.m 文件编译成 C++ 代码
注:其实 super 底层实现并不是调用 objc_msgSendSuper 函数,而是 objc_msgSendSuper2 ! C++ 代码只能作为 OC 底层实现的参考
② 如何证明 super 底层调用的是 objc_msgSendSuper2
方式一:给一个 super 方法打上断点,查看其汇编语言即可
汇编语言(部分)
方式二:查看 Runtime 源码
注:objc_msgSendSuper2 和 objc_msgSendSuper 两者参数不同,前者时当前类,后者则是当前类的父类。虽然两者传参不同,但最终的实现效果是一样的
方式三:打印内存地址间接证明 super 底层走的是 objc_msgSendSuper2 函数。在输出语句行打上断点,并分析出局部变量的内存分布状况,会包含 super 中的参数
使用 lldb 命令
3 是 obj 的内存地址,指向 Person 类对象
5 是 super 第一个参数 self 的内存地址,是 UIViewController 的实例对象
6 是 super 第二个参数 Class 的内存地址,是 UIViewController 类对象,再次证明了 super 底层调用的是 objc_msgSendSuper2
7 是一个未知的内存地址(可能已经出了该方法)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
2022-03-21 iOS章结 - 知道 \12
2018-03-21 iOS第三方 - SDWebImage
2018-03-21 iOS第三方 - FMDB