小碼哥iOS學習筆記第十五天: super面試題

1、面試題

  • 建立OC項目, 定義Person類, 添加name屬性和實現print方法, 以下圖所示

  • ViewController中的-viewDidLoad方法裏實現下面的代碼

問: 代碼是否會報錯? 是否可以順利運行? 是否能打印?, 若是能, 會打印什麼?面試

  • 運行程序, 能夠看到程序沒有報錯, 且正常運行, 並有以下打印

Person name is <ViewController: 0x7ff78ef08880>
複製代碼

問: 爲何這麼打印?bash

2、解析

  • 現有以下代碼

  • person是一個指針, 存儲着[[Person alloc] init]的地址, 因此person指向剛建立的Person實例對象

  • 咱們知道, 一個對象在底層就是一個結構體, 它的第一個成員變量是isa, 因此[[Person alloc] init]在底層就是下面的樣子

  • 由於isaPerson對象中的第一個成員變量, 因此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

super的底層結構

  • 查看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, 也就是當前控制器對象
相關文章
相關標籤/搜索