我们今天主要来探索一个iOS中常见的一个面试题。
假定我们有一个类LYPerson:
@interface LYPerson : NSObject
@property(nonatomic, copy) NSString *name;
- (void)saySomething;
@end
#import "LYPerson.h"
@implementation LYPerson
-(void)saySomething {
NSLog(@"name: %@", self.name);
}
@end
我们在ViewController中,定义如下方法
- (void)viewDidLoad {
[super viewDidLoad];
// 代码一
Class cls = [LYPerson class];
void *p1 = &cls;
void *sp = (void *)&self;
void *end = (void *)&p1;
[(__bridge id)p1 saySomething];
}
请问:
1,saySomething函数能成功调用么?
2,输出结果是什么?
分析
首先我们来看下如下代码的运行结果
// 代码二
LYPerson *person = [[LYPerson alloc] init];
person.name = @"LY";
[person saySomething];
对于代码二来讲:

LYPerson实例对象.png
LYPerson实例对象调用方法时,通过isa指针,在其类对象的methodlist里面进行方法查找,获取name属性值,是通过实例对象的首地址 偏移8字节,读取name的属性值。
了解了方法调用和属性的读取之后,我们在来分析代码一
- p1指针指向
LYPerson类对象
p1指针.png
所以 p1能够调用 saySomething方法。
紧接着,输出结果是什么呢?
name: <ViewController: 0x7ffa19c058f0>
为什么是 <ViewController: 0x7ffa19c058f0> ??????
首先我们来看下此时的栈空间里面的存储情况,我们陈述一些规则:
- 1,对于OC函数都有
两个隐形参数:self和_cmd。 - 2,
[super viewDidLoad]:在运行期会转化为objc_msgSendSuper({self, class_getSuperClass(objc_getClass("ViewController"))})函数,objc_msgSendSuper的参数为一个结构体,有两个成员变量,成员变量会从后向前依次入栈。
这样 self,_cmd,objc_msgSendSuper({self, "Viewcontroller"}) ,p1会依次入栈。

栈存储情况.png
在
saySomething方法中
-(void)saySomething {
NSLog(@"name: %@", self.name);
}
读取name属性值,根据LYPerson的内存分布,需要偏移8字节,根据此时的栈内存分布,读取的值为 为 ViewController实例。所以输出结果为 name: <ViewController: 0x7ffa19c058f0>。
我们对代码进行稍微改动下
- (void)viewDidLoad {
[super viewDidLoad];
LYPerson *p2 = [[LYPerson alloc] init];
p2.name = @"p2 name";
Class cls = [LYPerson class];
void *p1 = &cls;
void *sp = (void *)&self;
void *end = (void *)&p1;
[(__bridge id)p1 saySomething];
}
此时的栈空间的存储情况为

p2.png
所以说输出结果为name <LYPerson...>。
