分類與擴充
分類(Category)
Category是Objective-C 2.0之後新增的語言特性,分類、類別其實都是指的Category。Category的主要作用是為已經存在的類新增方法。
我們用分類都做了什麼事
- 宣告私有方法(我們定義一個分類,只把標頭檔案放在對應宿主檔案的.m當中,滿足私有方法的宣告和使用,對外不暴露)
- 分解體積龐大的類檔案(比如一個類的功能特別複雜,那麼我們可以按照功能來對類當中的方法進行分類,把同一個功能的方法放在一個分類檔案當中)
- 把Framewrok的私有方法公開化
特點
- 執行時決議(我們在編寫好分類的檔案後,它並沒有把分類的內容新增到宿主類上面,這個時候宿主類中還沒有分類中的方法,而是在執行時通過runtime把分類中新增的內容真實的新增到宿主類上面)
- 可以為系統類新增分類(比如我們經常會用到獲取UIView座標的分類方法,就是在對UIView新增了分類)
分類中可以新增哪些內容
- 例項方法
- 類方法
- 協議
- 屬性(其實只是宣告瞭get、set方法,並沒有新增例項變數,可以通過關聯物件來新增例項變數)
我們來看看分類的底層結構
struct category_t {
const char *name;//分類的名稱
classref_t cls;//分類所屬的宿主類
struct method_list_t *instanceMethods;//例項方法列表
struct method_list_t *classMethods;//類方法列表
struct protocol_list_t *protocols;//協議
struct property_list_t *instanceProperties;//例項屬性列表
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
我們通過分類的結構就可以更好的印證我們能夠為分類新增哪些內容,這裡並沒有例項變數的成員結構.
原始碼分析
objc-runtime-new.mm
這裡主要分析例項方法的新增邏輯.
我們從 remethodizeClass 這個方法開始分析
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertWriting();
//判斷當前類是否是元類物件,也就是判斷新增的方法是例項方法還是類方法
isMeta = cls->isMetaClass();
// 獲取cls中未完成整合的所有分類
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
if (PrintConnecting) {
_objc_inform("CLASS: attaching categories to class '%s' %s",
cls->nameForLogging(), isMeta ? "(meta)" : "");
}
//將cats拼接到cls上
attachCategories(cls, cats, true /*flush caches*/);
free(cats);
}
}
來看看attachCategories這個拼接分類到宿主類的方法
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if (!cats) return;//如果當前類沒有分類的話直接返回
if (PrintReplacedMethods) printReplacements(cls, cats);
//判斷當前類是否是元類物件
bool isMeta = cls->isMetaClass();
/*
二維陣列 [[method_t,method_t,...],[method_t],[method_t,method_t,method_t],...]
*/
method_list_t **mlists = (method_list_t **)
malloc(cats->count * sizeof(*mlists));//方法列表
property_list_t **proplists = (property_list_t **)
malloc(cats->count * sizeof(*proplists));//屬性列表
protocol_list_t **protolists = (protocol_list_t **)
malloc(cats->count * sizeof(*protolists));//協議列表
int mcount = 0;//方法
int propcount = 0;//屬性
int protocount = 0;//協議
int i = cats->count;//宿主類分類的總數
bool fromBundle = NO;
while (i--) {//倒序遍歷,最先訪問最後遍歷的分類.比如兩個分類有同名方法,那麼最後編譯的那個同名方法會最終生效
//獲取一個分類
auto& entry = cats->list[i];
//獲取該分類的方法列表
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
//最後編譯的分類最先新增到分類列表中
mlists[mcount++] = mlist;
fromBundle |= entry.hi->isBundle();
}
//屬性列表新增規則,同方法列表新增規則
property_list_t *proplist = entry.cat->propertiesForMeta(isMeta);
if (proplist) {
proplists[propcount++] = proplist;
}
//協議列表新增規則,同方法列表新增規則
protocol_list_t *protolist = entry.cat->protocols;
if (protolist) {
protolists[protocount++] = protolist;
}
}
// 獲取宿主類當中的rw資料,其中包含宿主類的方法列表資訊
auto rw = cls->data();
// 主要是針對 分類中有關於記憶體管理相關方法情況下的一些特殊處理
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
/*
methods代表類的方法列表,attachLists方法表示將mcount個元素的mlists(方法列表)拼接到rw(宿主類)的methods(方法列表)上
*/
rw->methods.attachLists(mlists, mcount);
free(mlists);
if (flush_caches && mcount > 0) flushCaches(cls);
rw->properties.attachLists(proplists, propcount);
free(proplists);
rw->protocols.attachLists(protolists, protocount);
free(protolists);
}
演示圖例
新增分類的流程
- 獲取分類列表的count,然後原來的類方法列表記憶體移動count
- 分類列表記憶體拷貝到原來的類方法列表的前方
- 同樣的方法,優先呼叫分類的方法
- 分類具有同樣的方法,根據編譯順序決定,取最後編譯分類的方法列表
擴充(Extension)
我們用擴充都做了什麼事
- 宣告私有屬性
- 宣告私有方法
- 宣告私有成員變數
擴充的特定
- 編譯時決議
- 只以宣告存在,沒有具體實現,寄生於宿主類.m中
- 不能為系統類新增擴充套件
分類、擴充套件區別
- 分類中原則上只能增加方法(能新增屬性的的原因只是通過runtime解決無setter/getter的問題而已)
- 類擴充套件不僅可以增加方法,還可以增加例項變數(或者屬性),只是該例項變數預設是@private型別的(用範圍只能在自身類,而不是子類或其他地方)
- 類擴充套件中宣告的方法沒被實現,編譯器會報警,但是類別中的方法沒被實現編譯器是不會有任何警告的。這是因為類擴充套件是在編譯階段被新增到類中,而類別是在執行時新增到類中。
- 類擴充套件不能像分類那樣擁有獨立的實現部分(@implementation部分),也就是說,類擴充套件所宣告的方法必須依託對應類的實現部分來實現。
相關文章
- 分類擴充套件套件
- iOS開發的分類和擴充套件iOS套件
- iOS分類(category)、類擴充套件(extension)、繼承的區別iOSGo套件繼承
- HttpContext擴充套件類HTTPContext套件
- 內容分類擴充套件性標籤設計套件
- 數論分塊擴充套件套件
- 無限級分類之Laravel-nestedset擴充套件包的使用Laravel套件
- [外掛擴充套件]onethink自定義分類標籤-關聯模型套件模型
- Java-IoUtil擴充套件工具類Java套件
- C# 反射呼叫擴充類方法C#反射
- weex ios擴充套件類的作用iOS套件
- Laravel MongoDB 資料庫查詢擴充外掛 擴充原始 Laravel 類LaravelMongoDB資料庫
- Lambda擴充套件與用途套件
- BSGS與擴充套件BSGS套件
- 五個檢視擴充套件類 LL套件
- dart系列之:dart類的擴充套件Dart套件
- PowerToys外掛擴充套件(類似Alfred)套件Alfred
- SpringBoot各類擴充套件點詳解Spring Boot套件
- Spring(11) - Introductions進行類擴充套件方法Spring套件
- C#中的擴充套件類的理解C#套件
- 轉向Kotlin——列舉類和擴充套件Kotlin套件
- 分頁查詢及其擴充應用案例
- 中文分詞 PHP 擴充套件 SCWS 安裝中文分詞PHP套件
- Android:ListView的擴充與進階AndroidView
- Solon詳解(六)- Solon的校驗擴充套件框架使用與擴充套件套件框架
- kotlin 擴充套件(擴充套件函式和擴充套件屬性)Kotlin套件函式
- 康擴充開的原理與實現
- Linq查詢語法與擴充方法
- 10.還不會擴充套件Dart中的類?套件Dart
- 【Kotlin】擴充套件屬性、擴充套件函式Kotlin套件函式
- BurpSuite 擴充套件開發[1]-API與HelloWoldUI套件API
- 關於Array.reduce的理解與擴充
- 使用cjieba(結巴分詞庫)實現php擴充套件中文分詞JiebaPHP套件中文分詞
- Laravel 執行時類的功能擴充套件的實現Laravel套件
- [提問交流]擴充套件裡的外掛如何歸類?套件
- 實現了所有主流APP的分類切換效果,可快速接入,靈活擴充套件(swift)APP套件Swift
- Django訊號與擴充套件:深入理解與實踐Django套件
- day88-ElasticSearch-分詞- 自定義擴充套件詞庫Elasticsearch分詞套件