今天看到一道有意思的面试题
@interface Sark : NSObject
@property (nonatomic, copy) NSString *name;
- (void)speak;
@end
@implementation Sark
- (void)speak {
NSLog(@"my name's %@", self.name);
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [Sark class];
void *obj = &cls;
[(__bridge id)obj speak];
}
@end
问:上面代码会打印什么东西?
what? Are you kidding me?
我们先来看看运行结果
居然还能真的打印出来。既然结果都告诉你了,我们就来分析一下。
第一点:为什么能调用speak方法?
- 1、
objc
我们知道在调用speak
的时候被强转成了id
类型, - 2、
obj
指向的内存地址的第一个属性是指针类型
,并且指向的是[Sark class]
,其实就是isa
指针 - 3、当OC对象
obj
调用方法speak
,其实是向obj
发送消息具体实现其实就是objc_msgSend(obj, @selector(speak))
,而isa
指针又指向[Sark class]
;
经过上面3点分析,我们不难得出obj
可以是可以找到speak
方法的
第二点:obj指向的内存结构是啥?(speak会输出啥?)
- 1、
cls
和obj
我们知道都是函数内的局部变量,应该在内存的栈
结构中
- 2、
[super viewDidLoad];
这个是OC对象调用父类的方法,其实现本质是objc_msgSendSuper2
函数,而其第一个参数类型是struct objc_super *
;他会隐式的创建一个objc_super
结构所以这段代码的大意可以表示为如下
struct objc_super arg = {
self,
objc_getClass("ViewController")
};
objc_msgSendSuper2(&arg, sel_registerName("viewDidLoad"));
至此其内存结构可以解析为
经过以上两点对内存结构的分析我们知道,当我们调用[(__bridge id)obj speak];
时会将viewController
的实例self
打印出来;
总结 :考擦的知识点
- 1、oc对象的内存结构分布情况
- 2、函数内的局部变量在栈中的分布情况,这里需要一点汇编知识
- 3、关键字
super
在底层的实现原理