面試驅動技術合集(初中級iOS開發),關注倉庫,及時獲取更新 Interview-seriesgit
說到iOS,要麼公司規模比較小,<=3人,不須要面試。github
其餘的,大機率要讓你刀槍棍棒十八般武藝都拿出來耍耍。面試
而其中,但凡敵軍陣營中有iOSer的,又極大機率會考到 Runtime 的知識點。bash
如下,是一題 sunnyxx的一道 runtime 考題,給大夥練練手,若是掌握了,Runtime層面的初中級問題應該都不在話下~函數
//MNPerson
@interface MNPerson : NSObject
@property (nonatomic, copy)NSString *name;
- (void)print;
@end
@implementation MNPerson
- (void)print{
NSLog(@"self.name = %@",self.name);
}
@end
---------------------------------------------------
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [MNPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
複製代碼
問輸出結果是啥,會不會崩潰。ui
最終結果:atom
self.name = <ViewController: 0x7fe667608ae0>
複製代碼
what?spa
[MNPerson alloc]init]
??當前內存地址結構 - 與正常的[person print]
對比3d
調用print 方法,不須要關心有沒有成員變量
_name
,因此能夠理解爲,cls == isa指針
print
函數問題2:爲啥裏面打印的是 ViewController
這就須要瞭解到iOS的內存分配相關知識
void test(){
int a = 4;
int b = 5;
int c = 6;
NSLog(@"a = %p,b = %p,c = %p",&a,&b,&c);
}
---------------------------
a = 0x7ffee87e9fdc,
b = 0x7ffee87e9fd8,
c = 0x7ffee87e9fd4
複製代碼
OC方法的本質,實際上是函數調用, 底層就是調用 objc_msgSend() 函數發送消息。
- (void)viewDidLoad {
[super viewDidLoad];
NSString *test = @"666";
id cls = [MNPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
複製代碼
以上述代碼爲例,三個變量 - test、cls、obj,都是局部變量,因此都在棧空間
棧空間是從高地址到低地址分配,因此test是最高地址,而obj是最低地址
MNPerson底層結構
struct MNPerson_IMPL{
Class isa;
NSString *_name;
}
- (void)print{
NSLog(@"self.name = %@",self->_name);
}
複製代碼
_name
成員變量,實際上是經過self ->
去查找;[(__bridge id)obj print];
即經過 obj 開始找;_name
,是經過指針地址查找,找得MNPerson_IMPL
結構體MNPerson_IMPL
裏面就兩個變量,因此這裏查找 _name
,就是經過 isa
的地址,跳過8個字節,找到 _name
而前面又說過,cls = isa,而_name 的地址 = isa往下偏移 8 個字節,因此上面的圖能夠轉成
_name的本質,先找到 isa,而後跳過 isa 的八個字節,就找到 _name這個變量
因此上圖輸出
self.name = 666
複製代碼
最先沒有 test變量的時候呢
- (void)viewDidLoad {
[super viewDidLoad];
id cls = [MNPerson class];
void *obj = &cls;
[(__bridge id)obj print];
}
複製代碼
底層 - objc_msgSendSuper
objc_msgSendSuper({ self, [ViewController class] },@selector(ViewDidLoad)),
等價於:
struct temp = {
self,
[ViewController class]
}
objc_msgSendSuper(temp, @selector(ViewDidLoad))
複製代碼
因此等於有個局部變量 - 結構體 temp,
結構體的地址 = 他的第一個成員,這裏的第一個成員是self
因此等價於 _name = self = 當前ViewController,因此最後輸出
self.name = <ViewController: 0x7fc6e5f14970>
複製代碼
**其實super的本質,不是 objc_msgSendSuper({self,[super class],@selector(xxx)}) **
而是
objc_msgSendSuper2(
{self,
[current class]//當前類
},
@selector(xxx)})
複製代碼
函數內部邏輯,拿到第二個成員 - 當前類,經過superClass指針找到他的父類,從superClass開始搜索,最終結果是差很少的~
友情演出:小馬哥MJ
題目來源: