美文网首页iOS 开发 Objective-C
iOS 底层 day15 一道OC的综合题目

iOS 底层 day15 一道OC的综合题目

作者: 望穿秋水小作坊 | 来源:发表于2020-09-07 22:40 被阅读0次
    题目内容
    • 上述代码能正常运行,打印内容如下:
    Demo[13193:1133563] My name is <ViewController: 0x7fe536c07830>
    
    • 提示:使用 实例对象在内存中的布局super 调用本质栈空间分配问题消息机制访问成员变量的本质 这些知识点来解决这个问题
    1. 问题解析,我们对上述题目,主要存在两点疑惑
    • 为什么 print 能够正常调用?
    • 为什么 self.name 变成了 ViewController?
    2. 为什么 print 能够正常调用?
    • id cls = [YYPerson class] 是拿到了 YYPerson 类对象的地址,放到指针变量 cls

    • void *obj = &cls 是定义一个指针变量 obj ,指向 &cls

    • [(__bridge id)obj print] 是将obj 强转成 id 类型,然后调用 print 方法

    内存结构图
    • 我们将 obj指针 看成 person指针 ,他们都指向一个地址
    • person指针 指向的内存,存放着 isa指针_name字符串指针,所以在内存中占用 16字节。
    • obj指针 指向 cls指针未知内容的8字节,在内存中占用16字节
    • clsisa 都指向 YYPerson类对象
    • 所以从实例和类对象直接的关系来看:obj 等价于 person
    • 因此 [(__bridge id)obj print] 能够正常打印
    3. 为什么 self.name 变成了 ViewController?
    • 从题目2的内存结构图中可以得知,我们 假实例占用16字节,本来应该是 _name指针 ,现在被未知的8字节内容占据了
    • 所以问题可以转换成 cls所在内存未知 后面连续的 8 字节内容是什么呢?
    4. 我们还需要了解 [super viewDidLoad]; super 的本质是什么?
    • 我们之前学过 super 的本质,所以上述代码的本质如下:
    objc_msgSendSuper({self, [UIViewController class]},@selector()viewDidLoad)
    
    • {self, [UIViewController class]} 是一个临时结构体
    5. 在此之前我们还补充相关知识
    • 静态变量: 会被放在内存的静态区
    • 常量: 会被放在内存的常量区
    • 程序员alloc出来的相关类实例:会被存放在堆区(这个数据结构里面的堆是两码事)
    • 函数的参数值、局部变量的值: 会被存放在栈区(这个栈的操作数据结构的栈很相似)
    6. 所以,我们可以绘制出 -viewDidLoad 函数内部的栈空间占用情况
    函数内部的栈空间占用情况
    • 由此我们可以得出紧挨着 clsself 会被我们的程序当成 _name指针,所以,我们的self.name 变成了 ViewController
    7. 修改代码验证我们的说法
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        NSString *abc = @"hahahaha";
        id cls = [YYPerson class];
        void *obj = &cls;
        [(__bridge id)obj print];
    }
    
    • 这时候在栈空间中 selfcls 中间会插入abc,按照我们的分析,abc 将会被我们的程序误认为是 self.name 打印出来.
    • 打印结果如下:
    Demo[13379:1187071] My name is hahahaha
    
    8. 我们通过Xcode打印栈空间的地址,取得地址上的内容,来侧面印证我们的想法
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        id cls = [YYPerson class];
        void *obj = &cls;
        NSLog(@"断点处");
        [(__bridge id)obj print];
    }
    
    • 打印如下
    (lldb) p/x &obj
    (void **) $1 = 0x00007ffeedb16a70
    
    (lldb) x/4g 0x00007ffeedb16a70
    0x7ffeedb16a70: 0x00007ffeedb16a78 0x00000001020e8ea0
    0x7ffeedb16a80: 0x00007fa4d3004bc0 0x00000001020e8e28
    
    (lldb) po 0x00007ffeedb16a78
    <YYPerson: 0x7ffeedb16a78>
    
    (lldb) po 0x00000001020e8ea0
    YYPerson
    
    (lldb) po 0x00007fa4d3004bc0
    <ViewController: 0x7fa4d3004bc0>
    
    (lldb) po 0x00000001020e8e28
    ViewController
    
    • 四个地址,分别对应 obj cls self [ViewController class]

    • 打印出来的内容与我们的猜想一致,更好的从侧面印证我们的说法。

    • x/4g 0x00007ffeedb16a70 是从 0x00007ffeedb16a70 地址开始,每组的内容占据内存的8个字节,打印4组内容

    相关文章

      网友评论

        本文标题:iOS 底层 day15 一道OC的综合题目

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