首先我們先來看一下這道面試題是啥?
題目看著非常簡單,我是先創建了一個繼承NSObject的GDPerson類;
GDPerson類的.h文件
GDPerson類的.m文件
再看一下我們viewController.m里面的代碼:
作為一個開發者,有一個學習的氛圍跟一個交流圈子特別重要,這是一個我的iOS開發交流群:130 595 548,不管你是小白還是大牛都歡迎入駐 ,讓我們一起進步,共同發展!(群內會免費提供一些群主收藏的免費學習書籍資料以及整理好的幾百道面試題和答案文檔!)
這是題目
請問:
1.print能不能調用成功?如果不能會怎么樣?如果能的話調用結果是什么?
這個又是一個更扯的面試題,真正開發的時候,誰也不會這么寫,這個還是主要考你基礎!相信你看到這個題目之后應該心中已經有了答案,要不知道結果,要么可能知道結果,要么猶豫不決,要么不知道哈哈!
其實這個面試題考點比較多,考點如下:
1.你要了解super的本質,第一個參數要傳結構體
2.函數的堆棧分配問題
3.消息機制,調用方法是怎么調用
4.訪問成員變量的本質
這樣,我們先來看一下調用結果吧!
請看結果:
面試題運用結果
這里跟你想的答案一樣嗎?
這樣我在cls前面加一段代碼,我們再看一下結果:
面試題運用結果
首先我們立刻會有2個疑問:
1.為什么能調用成功?
2.為什么self.name調用結果是viewController?
一.為什么能調用成功?
[(__bridge id)obj print];由之前的學習,我們知道這個代碼的本質是:給obj發一條print的消息,就會去通過obj的isa找到obj的類對象,去找方法列表,這個是非常清楚的.這個能調用成功,說明(__bridge id)obj也存在我們之前說的是isa指針這個東西
我畫個圖,這樣理解的比較清楚.
cls是指向GDPerson,obj是指向cls,所以圖如下:
上面綠色是[GDPerson class],圖比較模糊
再請看下面的代碼:
person指向GDPerson的實例變量,而GDPerson的實例變量是包括isa和成員變量等等,這個也很清楚.而isa是指向GDPerson的類對象,所以請看下面的圖:
我們根據之前的源碼分析知道,isa和_name是存在一個結構體,而對于結構體來說,第一個成員變量的地址值就是這個結構體的地址.所以person就是指向isa.
好了,這兩個圖我們分析清楚了以后,你看這兩個圖是不是很類似,幾乎是一樣的,我們再看下面的一個圖:
上面綠色是[GDPerson class],圖比較模糊
所以從上面的結構上,你看是不是就是一樣的,obj就相當于person,所以能調用,這個比較抽象.說白了,你上面寫的cls就是isa作用,因為我們知道指向類對象的指針就是isa.isa就是存儲類對象的地址值.而你可能有疑問cls里面都沒有_name怎么能算是GDPerson對象呢?注意,我們是調用print方法,調用方法沒有說一定有_name成員變量!是這樣吧!它是跟有沒有成員變量是沒有關系的.
第二個角度解釋:obj怎么找到cls,就是通過cls對象的前8個字節的內存地址找到它,前8個字節也是結構體的地址,通過地址就能很容易找到class對象,是這樣的.所以它能夠調用成功!這就是調用的本質,后面那條線的調用也是如此.
二.為什么self.name調用結果是其他的?
首先你要知道堆棧排列的知識點,請看下圖:
這些變量都是存在棧空間的,而且內存地址是由高地址到底地址.
好我們再看下面這個之前的圖:
我們畫一下這些地址排列如下
上面代碼的結構示意圖
上面綠色是[GDPerson class],圖比較模糊,這個圖很清楚.
現在我們來回憶一下:之前我們定義一個對象,比如上面的GDPerson這個類,它的底層生成的結構是下面這個樣子的
structGDPerson_IMPL
{
Class isa;
NSString *_name;
}
上面這個結構體就是底層的結構,現在我們想一下,怎么找到_name這個地址,肯定是找到isa指針的地址加上8個字節就能找到_name吧?看下圖
這個應該是很明顯,找name就是通過isa找到name對應的這塊內存地址就行了.
現在大家知道下結果了吧?上面的cls就是我們的isa指針,所以找name就找到了test這里面!好我們再運行一下看看
這里你定義test,你定義任何其他的都是一樣,都會找到cls前面聲明的變量.比如我再定義一個objct再看下.
輸出的結果就是cls上面最近的一個創建的.還有一個未解決就是self.name調用結果是viewController
三、為什么self.name調用結果是viewController?
我們把test變量去掉,結果就會是viewController 我直接說了這個主要是[super viewDidLoad]影響;從上一張博客我們知道
super做了什么事它底層是這樣實現的(上個博客說得很清楚): objc_msgSendSuper({ self,[UIViewController Class]} ,@selector(viewDidLoad));其他就是做了這件事@selector(viewDidLoad)也可以寫出sel_registerName("viewDidLoad")
這個肯定要開始定義一個局部的結構體才能傳入 objc_msgSendSuper這個方法.所以最高地址是abc這個結構體,而結構體的第一個參數的地址就是結構體的地址,所以輸出的就是self也就是viewController.
如下圖:
就會找到self
下面我們通過內存來證明一下這個東西:
這個題目涉及的知識點還是比較多,如果直接給你題目憑空想想,還是很難想出答案,好了,就說這么多了