• Runtime的本质(五)


    面试题:

    在这里插入图片描述

    答案

    my name is <ViewController: 0x15f2026e0>
    

    首先,我们看下

    YZPerson *person = [[YZPerson alloc] init];
    [person print];
    

    两句代码在内存方面的存储状态:
    person指针 指向 YZPerson建立的实例对象。

    我们知道,[[YZPerson alloc] init]建立的实例对象,是一个结构体。结构体里面至少有一个指针,isa。还有一个成员变量_name。而且,isa是结构体的第一个成员,因此,可以说person指针 指向 YZPerson建立的实例对象的isa指针。

    而,isa指向的是YZPerson类对象。

    也就是,person实例对象,通过指针,找到指针指向的内容,取出前8个字节,再根据前8个字节里面的内容当做地址(isa),找到YZPerson类对象,进而在类对象里面找到print方法。

    然后再看下面试题的内存存储状态:
    obj->cls->YZPerson类对象
    在这里插入图片描述

    为什么可以打印?

    person指针指向的地址cls,取出前8个字节,以前8个字节的内容为地址,找到的地方,就当做YZPerson类对象。然后在类对象里面找到print方法。因此,cls就相当于isa指针,因此,可以执行print方法。

    为什么打印的是my name is <ViewController: 0x15f2026e0>

    在看打印结果之前,我们先看一个其他小荔枝:

    void test()
    {
        long long a = 4;
        long long b = 5;
        long long c = 6;
        NSLog(@"%p, %p, %p", &a, &b, &c);
    }
    结果:
    0x16bbd5c58, 0x16bbd5c50, 0x16bbd5c48
    

    首先,我们知道,局部变量存储在栈上。
    从结果来看,栈空间分配,内存地址是从高到低。

    回到一开始的面试题,可以看出,cls的内存地址是最大的,也就是,写在最前面的,内存地址最大。

    在正常情况下:
    self指的是person,print方法里面的self.name,其实就是self->_name。

    如何通过self找到_name?

    struct YZPerson_IMPL
    {
        Class isa;
        NSString *_name;
    }
    

    self(person)指向isa,然后去掉前8个字节(isa),查找后面8个字节,后8个字节的内容,就是_name的内容。
    严禁来说,是isa指针地址,+8

    而在本例中,self是消息接收者,而通过[(__bridge id)obj print];可以知道,消息接收者就是obj。
    obj指向的cls内容地址+8,因为栈地址是从高到底,+8正好是cls前面的代码被当做_name。
    obj指向的cls,cls+8
    那么,如果

    NSString *test = @"123";
    id cls = [YZPerson class];
    void *obj = &cls;
    [(__bridge id)obj print];
    

    那么打印的内容是
    my name is 123
    也就是,谁在cls前面,打印的就是谁的指针指向的内容。

    而面试题cls前面是[super viewDidLoad];
    [super viewDidLoad]
    等价于:

    objc_msgSendSuper({self,  [UIViewController class]}, @selector(viewDidLoad));
    

    然后,{self, [UIViewController class]}这个是局部变量,相当于有一个隐式的局部变量:

    strut abc = {self,  [UIViewController class]};
    objc_msgSendSuper(abc, @selector(viewDidLoad));
    

    这样,cls前面的第一个指针是一个结构体,第一个指针是self。因此,打印的是self指向的内容,self指向的就是viewcontroller。

    在这里插入图片描述

    super更深学习

    我们知道,super通过指令,可以看到编译后转换为objc_msgSendSuper({self, [UIViewController class]}, @selector(viewDidLoad));
    但,其实在运行的时候,不是这个类型。
    在[super viewDidLoad]处打断点,然后通过Debug->Debug Workflow->Always show Disassembly可以看到汇编。
    在这里插入图片描述

    在这里插入图片描述

    可以看到,调用的是objc_msgSendSuper2。在源码中查找这个方法,可以看到:

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    虽然Class传的是当前类,但是汇编实现,是class->superclass。
    也就是说,方法不一样,参数传的不一样,但最后实现的结果是一样的。

    也就是,super里面的结构体第二个参数,是自己。。。不是其父类类型。虽然转换为底层代码显示是父类,但实际执行的时候,就是自己。

    objective-c在变为机器代码之前,会被LLVM编译器转换为中间代码(Intermediate Representation)

    可以使用以下命令行指令生成中间代码
    clang -emit-llvm -S main.m

    Xcode软件是以LLVM为编译器
    OC->中间代码->汇编、机器代码

  • 相关阅读:
    参数方法HttpServletRequest之getParameter
    nullnull单例的实现方式之GCD方法
    阻带窗函数[数字信号处理]使用窗函数设计FIR滤波器
    nullnullhdu4450Draw Something
    第三方设置XML解析:第三方库GDataXMLNode的使用
    class左边nbu 2414 Please help the poor donkey!
    数据可视化可视化营养含量
    速度坐标hdu4445Crazy Tank
    singletonclassDesign Patterns: Singleton Basics 设计模式:单例基础
    控制台权限MMC不能打开文件 SQL Server Enterprise Manager.MSC 或权限不够 (解决方法 )
  • 原文地址:https://www.cnblogs.com/r360/p/15891527.html
Copyright © 2020-2023  润新知