OC
項目, 定義Person
類, 添加name
屬性和實現print
方法, 以下圖所示ViewController
中的-viewDidLoad
方法裏實現下面的代碼問: 代碼是否會報錯? 是否可以順利運行? 是否能打印?, 若是能, 會打印什麼?面試
Person name is <ViewController: 0x7ff78ef08880>
複製代碼
問: 爲何這麼打印?bash
person
是一個指針, 存儲着[[Person alloc] init]
的地址, 因此person
指向剛建立的Person
實例對象isa
, 因此[[Person alloc] init]
在底層就是下面的樣子isa
是Person
對象中的第一個成員變量, 因此isa
的地址與[[Person alloc] init]
的地址相同person
調用-print
方法時, 本質是經過isa
找到[Person class]
對象, 而後找到-print
方法來調用, 因此指針關係圖以下-print
方法中, 有打印成員變量_name
, 因此須要經過[[Person alloc] init]
找到_name
存儲的值isa的地址 + 8
找到_name
的地址, 而後訪問_name
存儲的內容obj
能調用print
方法的緣由id cls = [Person class];
複製代碼
cls
指針指向Person
的類對象obj
存儲着cls
的地址void *obj = &cls;
複製代碼
obj
指向cls
person
指向相似, 有下面這種指針結構[(__bridge id)obj print];
複製代碼
在結構上ui
person
調用方法的流程是: person->isa->[Person class]
obj
調用的流程與person
相似. obj->cls->[Person class]
這就是obj
可以調用-print
方法的緣由spa
0x7ffeeec0c9d8
0x7ffeeec0c9d0
0x7ffeeec0c9c8
複製代碼
能夠看到, 連續的三個局部變量, 在棧中的存儲順序是連續的3d
接着咱們看下面的這種狀況, 在cls
前面添加test
變量, 運行看一下打印指針
obj
經過找到cls
, 而後用cls的地址+8
, 找到的就是test
的地址, 因此打印時, 找到的_name
就是test
的值123
那麼面試題中的打印爲何是
ViewController
的實例對象呢?code
ViewController.cpp
底層結構[super viewDidLoad];
這一句在底層代碼是下面這一句((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
複製代碼
objc_msgSendSuper({self, class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
複製代碼
{self, class_getSuperclass(objc_getClass("ViewController"))}
cls的地址+8
, 找到的就是self
, 也就是當前控制器對象