这道面试题大部分应该都见过 https://halfrost.com/objc_runtime_isa_class/
@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Sark
-(void)speak{
NSLog(@"*******my name is %@",self.name);
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"ViewController = %@ , 地址 = %p", self, &self);
//这个是获得对应类
id cls = [Sark class];
// NSLog(@"Sark class = %@ 地址 = %p", cls, &cls);
//获得类地址
void *obj = &cls;
// NSLog(@"Void *obj = %@ 地址 = %p", obj,&obj);
[(__bridge id)obj speak];
}
问题在于这行代码
[(__bridge id)obj speak];
代码会执行,打印结果为"my name is viewcontroller";
obj 这时已经被强制转换为一个id 类型的对象, 它的isa 指针指向Sark类所在的地址, 所以它可以执行speak 方法.
但是为什么打印出来的是viewcontroller;
我查的资料大多是类似这样的话
这里牵涉到两个知识点:
1. 栈的入参
2.成员变量的访问
栈的入参:
这里是我理解: 栈中存放的都是对象的地址和基本数据类型; 我们每一次调用方法, 都会在栈中放入参数.
就是链接博客中的栈的参数示意图;
成员变量的访问:
我的理解:
这个obj 有isa 指针, 指向了Sark类所在空间,但是这个obj 和new /alloc 的Sark 实例对象不一样,这个obj 在堆内是没有空间的.
访问属性
属性 = ivar(成员变量) + setter + getter ;
通过 对象地址 + 基类大小 + ivar 字节偏移量(offset) 得到ivar的地址, 从而访问对应的值;
在我的理解中: 因为访问一个属性的值(ivar 的值),就是按照上述方式进行, 所以会得到一个地址, 只要这个地址有值,那么它就会打印出对应的东西;
上面那句话没有错,但是在这里没有讲清楚. 访问一个对象的成员变量 = &obj + offset(). 这里也同样如此,所以这个时候访问sark 中的name 属性时, 是沿着对象,向上移动8个字节.
为什么向上? 因为这这个对象在栈区! 栈区是从高往低排列.
而为什么是 viewcontroller ?
那么在访问name 这个属性时, 其实就是从obj 地址在栈上偏移 8个字节(NSString) , 所以 它会打印出obj 上一个的变量;
当你在 cls 之前加上一行代码后
NSLog(@"ViewController = %@ , 地址 = %p", self, &self);
NSString *str1 = @"123";
//这个是获得对应类
id cls = [Sark class];
// NSLog(@"Sark class = %@ 地址 = %p", cls, &cls);
//获得类地址
void *obj = &cls;
// NSLog(@"Void *obj = %@ 地址 = %p", obj,&obj);
[(__bridge id)obj speak];
打印的结果就是: *******my name is 123
因此, 它总是会打印出obj 这个对象栈之前8个字节(一个指针)的 地址 所 对应的 值.
这是在OC 层面上的解释, 加入再深入一点到C或者栈, 我觉的还需要学习.
https://www.cnblogs.com/clover-toeic/p/3755401.html
但是如果你在这之前放置的是一个int/NSInteger 类型, 会发生野指针错误. 没有找到一个确定的说法, 我的猜想是: self.name 是指针便宜, 但是int/NSInteger 是基本数据类型, 通过指针访问出现了错误.
这里只是猜想, 没有进行确认,若有人知道,望不吝赐教.