我們今天主要來探索一個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函數(shù)能成功調(diào)用么?
2,輸出結(jié)果是什么?
分析
首先我們來看下如下代碼的運行結(jié)果
// 代碼二
LYPerson *person = [[LYPerson alloc] init];
person.name = @"LY";
[person saySomething];
對于代碼二來講:

LYPerson實例對象.png
LYPerson實例對象調(diào)用方法時,通過isa指針,在其類對象的methodlist里面進行方法查找,獲取name屬性值,是通過實例對象的首地址 偏移8字節(jié),讀取name的屬性值。
了解了方法調(diào)用和屬性的讀取之后,我們在來分析代碼一
- p1指針指向
LYPerson類對象
p1指針.png
所以 p1能夠調(diào)用 saySomething方法。
緊接著,輸出結(jié)果是什么呢?
name: <ViewController: 0x7ffa19c058f0>
為什么是 <ViewController: 0x7ffa19c058f0> ??????
首先我們來看下此時的??臻g里面的存儲情況,我們陳述一些規(guī)則:
- 1,對于OC函數(shù)都有
兩個隱形參數(shù):self和_cmd。 - 2,
[super viewDidLoad]:在運行期會轉(zhuǎn)化為objc_msgSendSuper({self, class_getSuperClass(objc_getClass("ViewController"))})函數(shù),objc_msgSendSuper的參數(shù)為一個結(jié)構(gòu)體,有兩個成員變量,成員變量會從后向前依次入棧。
這樣 self,_cmd,objc_msgSendSuper({self, "Viewcontroller"}) ,p1會依次入棧。

棧存儲情況.png
在
saySomething方法中
-(void)saySomething {
NSLog(@"name: %@", self.name);
}
讀取name屬性值,根據(jù)LYPerson的內(nèi)存分布,需要偏移8字節(jié),根據(jù)此時的棧內(nèi)存分布,讀取的值為 為 ViewController實例。所以輸出結(jié)果為 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];
}
此時的??臻g的存儲情況為

p2.png
所以說輸出結(jié)果為name <LYPerson...>。
