内存地址分配的面试题

本文深入探讨了Objective-C中如何通过类对象指针动态调用实例方法的细节,解析了方法调用成功的原因及内存布局,揭示了self.name在不同上下文中的指向差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面的代码能编译执行吗?如果能执行的话,打印是什么?

@interface MJPerson : NSObject
@property (nonatomic, strong) NSString *name;
- (void)test;
@end

@implementation MJPerson
- (void)test
{
    NSLog(@"my name is %@",self.name);
}
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    id cls = MJPerson.class;
    void *obj = &cls;
    [(__bridge id)obj test];
}

能执行,打印结果是my name is <ViewController: 0x7f99bac0e740>
分析:
实例方法 test 是存在于类对象MJPerson的方法列表中的,obj是个指针,指向MJPerson类对象的指针,所以能调用成功 ; 如果MJPerson中压根没有这个方法的实现并且没有针对这个方法做动态解析和消息转发,肯定就不能调用成功
在内存分配的时候,局部变量cls,obj都是分配在栈上的,并且从高地址向低地址分配,并且占8个字节的长度,再分析test方法中的打印self.name,成员属性是存在于成员对象中的,在成员属性中除了_name外还存在一个isa指针,isa指针指向类对象,占据8字节单位,并且在高位地址值,name的地址值紧挨着也占据8个字节单位,所以obj指针调用test方法,打印的self->name,其实这个地址值是此时指向的MJPerson类在往上数8位的高地址空间

	NSObject *obj2 = [[NSObject alloc] init];
    id cls = MJPerson.class;
    void *obj = &cls;
    [(__bridge id)obj test];
    这个时候,obj2在高位,占据8字节,cls紧挨着,所以此时的打印结果会是my name is <NSObject: 0x7f99bac0e740>

在这里插入图片描述
至于一开始打印的ViewController,则是因为方法 [super viewDidLoad]的原因,这个方法的本质是

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

等价于

struct str = {
	self,
	[UIViewController class]
};

objc_msgSendSuper(str,@selector(viewDidLoad));

此时的内存分配图如下
在这里插入图片描述
cls上的高地址前一位存储的是self,所以打印ViewController

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值