本系列部落格是本人的原始碼閱讀筆記,如果有iOS開發者在看runtime的,歡迎大家多多交流。為了方便討論,本人新建了一個微信群(iOS技術討論群),想要加入的,請新增本人微信:zhujinhui207407,【加我前請備註:ios 】,本人部落格http://www.kyson.cn 也在不停的更新中,歡迎一起討論
引言
相信接觸過iOS的同學對runtime或多或少都有耳聞。 建立一個物件:
[[NSObject alloc] init];
複製程式碼
點選進入其定義:
可以發現其進入了檔案NSObject.h
中
右擊該檔案,選擇:show in Finder
可以看到runtime
暴露給我們的檔案:
雖然暴露給我們的不多,但其實已經能提供給我們很多功能,剛剛我們的物件建立就是一個典型的功能。
眾所周知,NSObject
物件是Objective-C語言中幾乎所有物件的根類。換言之,任何一個物件的建立都是通過runtime實現的,僅此一點,runtime的重要性可見一斑,現在的iOS面試也越來越通過對runtime的面試來區分iOS開發人員的水平高低。
筆者研究runtime原始碼有一段時間了,隨著研究的深入,對runtime的實現也越來越感興趣,因此想寫一套系列教程來和大家討論runtime的底層實現。
本文完整版詳見筆者小專欄:xiaozhuanlan.com/runtime
下載原始碼
蘋果開源了runtime的實現,在網站 opensource.apple.com/source/objc… 中可以找到各個版本的runtime原始碼。 但提供的是一個個檔案,不方便打包下載,網站 opensource.apple.com/tarballs/ob…中提供了壓縮包的下載。
編譯
下載下來的runtime原始碼是執行不了的,缺少一些依賴檔案,找起來也比較繁瑣。這裡筆者fork了一份,供大家參考(該專案編譯過程大家可以參考這篇文章:objc - 編譯Runtime原始碼objc4-680):
如圖,開啟工程後選擇工程debug-objc,點選run即可。由於debug-objc依賴於objc(即runtime的原始碼編譯的庫),因此我們在main函式中所有Objective-C的程式碼會呼叫我們編譯的runtime,從而方便我們除錯。
目錄分析
runtime原始碼目錄結構如下:
-
include
資料夾是我們引入的專案需要的依賴檔案 -
Public Headers
資料夾是對外暴露的,點開後我們不難發現,和文章開頭給出的檔案列表一模一樣: -
Private Headers
從字面意思瞭解,是私有的一些方法 -
Project Headers
runtime專案中會用到的標頭檔案 -
Obsolete Headers
一些孤立
的檔案,大部分可刪,只有hashtable2.h
的檔案會被其他檔案使用到。 -
Obsolete Source
無實質用處,可全刪 -
Source
目錄,是runtime的實現檔案集合,後面的文章主要是研究這個目錄。
小試牛刀
在我們的main.m中,輸入以下程式碼:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [[NSObject alloc] init];
}
return 0;
}
複製程式碼
打斷點後,我們會發現NSObject *obj = [[NSObject alloc] init];方法最終會呼叫到runtime中的NSObject.mm中。也就是說,物件的建立都是在NSObject.mm中完成的。 具體實現流程,會在後面的文章中逐步揭曉。
注意
有人反饋專案編譯不能通過,報如下錯誤:
dyld: Symbol not found: _objc_debug_taggedpointer_obfuscator
Referenced from: /usr/lib/system/libxpc.dylib
Expected in: ~/Library/Developer/Xcode/DerivedData/objc-ehlaekvxhzkjtdcmtvyuyxuygmfk/Build/Products/Debug/libobjc.A.dylib
in /usr/lib/system/libxpc.dylib
複製程式碼
原因是因為系統版本太高,與runtime版本不相容導致的。目前最新的runtime可以在這裡下載: objc4-750適用於系統macOS Mojave。
參考
dyld: Symbol not found: _objc_debug_taggedpointer_obfuscator