常見面試題:介紹一下分類,能用分類做什麼?內部是如何實現的?它爲什麼會覆蓋掉原來的方法?
深入瞭解Category
我們都知道OC代碼執行時會先轉成C\C++代碼,OC對象轉成對應的結構體; Category對應的結構體我們可以通過將分類的.m文件轉成c++文件查看: xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc LZPerson+text.m
即可生成對應的.cpp文件; 在.cpp文件中搜索Category_t可以看到Category對應的結構體:
-
struct _category_t {
-
const char *name;
-
struct _class_t *cls;
-
const struct _method_list_t *instance_methods;
-
const struct _method_list_t *class_methods;
-
const struct _protocol_list_t *protocols;
-
const struct _prop_list_t *properties;
-
};
-
複製代碼
通過觀察結構體我們可以發現分類對應的結構體中有存儲實例方法的列表、存儲類方法的列表、存儲協議的和屬性的列表。 到此可知面試題部分答案:
分類一般用來動態的爲已經存在的類擴展新的方法。 分類中可以添加實例方法、類方法、屬性、協議。但是不能添加成員變量。 添加屬性的話只會生成set、get方法的聲明不會實現也不會生成下劃線成員變量。
如何‘覆蓋’了原來的方法?
首先我們得知道:方法的調用就是消息的發送,如果調用實例方法,就是通過isa去類對象中查找對應的方法,如果調用類方法、就是通過isa去元類對象中查找對應的方法。
但是加入分類後通過isa去對應類或元類中查找時卻能查到分類中寫的方法!
所以我們可以得知:系統是在運行時把分類中的信息整合到了原來的類中!
到底推測是否正確以及如何整合,我們可以通過探究運行時的源碼來分析。 源碼下載地址:https://opensource.apple.com/tarballs/ 搜索objc4下載最新源碼。
這裏不張貼源碼代碼,只是把一步步的入口寫出來,有興趣的可根據以下的步驟去研讀源碼。
-
打開源碼後首先找到Runtime的入口文件objc-os.mm然後找到初始化方法-objc_init(void)
-
->map_images()//鏡像加載
-
->map_images_nolock()
-
->_read_images
-
->下滑找到//Discover categories
-
->remethodizeClass//重置類的方法
-
->attachCategories//綁定分類
-
->attachLists
-
->最核心的函數memmove和memcopy
-
複製代碼
memmove:將原來類中的信息列表在內存中向後移動,移動的大小就是分類中的信息所佔大小 memcopy:將分類中的信息複製到上一步移動出來的空間。
通過源碼分析可知:系統是在運行時將分類中對應的實例方法、類方法等插入到了原來類或元類的方法列表中,且是在列表的前邊!所以,方法調用時通過isa去對應的類或元類的列表中查找對應的方法時先查到的是分類中的方法!查到後就直接調用不在繼續查找。這即是’覆蓋’的本質!
存在多個分類,調用誰?
當有多個分類時,會調用哪個分類中的方法呢?
這個是與編譯順序有關,最後編譯的分類中對應的信息會在整合在類或元類對應列表的最前邊。所以是調用最後編譯的分類中的方法!可以查看Build Phases ->Complie Source 中的編譯順序!