通過之前博客的介紹,這個博客我們來介紹objc_msgSend,相信很多小伙伴在面試的時候,經常遇到面試官問:你知道runtime的消息機制嗎?等等關于runtime的知識點,學會了runtime不止讓我們面試中能增加亮點,也會為我們開發(fā)中提高很多便利!
通過這個博客你將學習到消息機制的三大階段的消息發(fā)送具體是怎么執(zhí)行的,詳細的介紹底層甚至匯編語言執(zhí)行的過程,詳細仔細學習下來你會收獲不少!好了,話不多說,lets begin!
objc_msgSend三大階段:消息發(fā)送礼患、動態(tài)方法解析、消息轉發(fā)
首先我們還是先由簡單到復雜,前提引入
其他的基礎知識我就不介紹了,直接進入正題,請看下面的代碼:我是創(chuàng)建了一個繼承NSObject的GDPerson的類,創(chuàng)建了personTest方法,里面打印了方法名字如下:
現在把上面的代碼轉成c++代碼,我們看一下底層是怎么實現的(劃到最下面,找到main函數實現)如下圖:(?xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp)
很清楚的看到,調用personTest就是通過消息機制objc_msgSend來處理的,現在我們看一下sel_registerName("personTest")這是什么鬼?你可以看一下定義,其實和之前的@selector(personTest),是一摸一樣,接下來我們證明一下
sel_registerName("personTest") ==?@selector(personTest)
請看下圖:
可以看出地址都是一摸一樣,說明完全是等價的.
所以oc中的調用方法是通過objc_msgSend來發(fā)送的,好,接下來我們看看重點.
objc_msgSend的執(zhí)行流程
objc_msgSend的執(zhí)行流程可以分為三大階段
1.消息發(fā)送
首先我們來源碼跟讀一下,下載蘋果objc最新源碼,為這里下載的是787(蘋果源碼下載地址)
首先全局搜索:objc_msgSend
最終我們會找到arm64里面的(iphoneos是arm64位),ENTRY是入口,接下來我們就大致看一下匯編的實現如下:
雖說看不懂具體的,大致我們還是能看懂,我這樣一標記,應該是很清晰了,接下來我們看一下?
CacheLookup NORMAL, _objc_msgSend,記住這個NORMAL,我們直接搜索CacheLookup,看名字我們都知道是緩存查找,接下來請看下面的截圖
接著就找CheckMiss的實現,看看它是怎么操作
接下來我們就查找?__objc_msgSend_uncached (故名思義是uncached,也就是沒找到緩存)
接著就在MethodTableLookup里面找,也就是方法列表里面找,因為緩存沒找到,我們繼續(xù)看實現
然后我們再在這個文件搜索_lookUpImpOrForward方法,你會發(fā)現找不到了(這個不截圖了),這個就是匯編的最后一步,那怎么辦呢?我們就去全局搜索,看看c++語言有沒有實現,這個時候你會發(fā)現能找到(注意_lookUpImpOrForward,在匯編語言會多一個_,我們平時項目打印的時候也能看到,所以你搜索的時候直接搜lookUpImpOrForward),還是搜索實現,你會發(fā)現你能找到如下的代碼:
再看下面的實現,還是上面那張圖的方法實現里面
上面那個 getMethodNoSuper_nolock 這個就是我們經常說的,拿到這個sel到類對象里面去查找方法,一會我們再看怎么去類對象里面查找,先看其他的,接下來我們就會去看log_and_fill_cache這個方法的具體實現(聽名字也知道是fill_cache,找到以后填充緩存)請看下圖
接著我們看cache_fill方法的實現:
接下來看insert實現:
到這里基本的流程,第一次階段的消息發(fā)送就走完了!我們再看
getMethodNoSuper_nolock如何查找method
繼續(xù)查找search_method_list_inline
findMethodInSortedMethodList 是查找排好序的查找.
好了,整個消息機制的第一個階段消息發(fā)送的底層源碼就是這些,
接下來我們總結一下上面說的流程
2.消息發(fā)送的流程總結:
接下來我用一張圖來解釋這個流程
如果你在記得類中,自己的緩存中以及父類的緩存中,父類中都找不到這個方法,那怎么辦呢?這就會到我們objc_msgSend第二階段動態(tài)方法解析,剩下的下一篇博客說吧,一次寫太多,寫得累,讀得也累,哈哈!