首先我們先來看一下這道面試題是啥?
題目看著非常簡單,我是先創建了一個繼承NSObject的GDPerson類;

GDPerson類的.h檔案

GDPerson類的.m檔案
再看一下我們viewController.m里面的代碼:

這是題目
請問:
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
下面我們通過記憶體來證明一下這個東西:

這個題目涉及的知識點還是比較多,如果直接給你題目憑空想想,還是很難想出答案,好了,就說這么多了
接下來我會繼續介紹runtime的實戰應用,來繼續學習runtime.
如果覺得我寫得對您有所幫助,請關注我,我會持續更新😄
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/271997.html
標籤:其他
下一篇:認識并且使用git
