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->中间代码->汇编、机器代码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值