1卒落、什么是Category?
category是Objective-C 2.0之后添加的語言特性诗芜,別人口中的分類、類別其實都是指的category竣贪。category的主要作用是為已經(jīng)存在的類添加方法帚湘。除此之外玫荣,apple還推薦了category的另外兩個使用場景。
可以把類的實現(xiàn)分開在幾個不同的文件里面大诸。這樣做有幾個顯而易見的好處捅厂。
可以減少單個文件的體積
可以把不同的功能組織到不同的category里
可以由多個開發(fā)者共同完成一個類
可以按需加載想要的category
聲明私有方法
apple 的SDK中就大面積的使用了category這一特性。比如UIKit中的UIView资柔。apple把不同的功能API進(jìn)行了分類焙贷,這些分類包括UIViewGeometry、UIViewHierarchy建邓、UIViewRendering等盈厘。
2、category特點(diǎn)
category只能給某個已有的類擴(kuò)充方法官边,不能擴(kuò)充成員變量沸手。
category中也可以添加屬性外遇,只不過@property只會生成setter和getter的聲明,不會生成setter和getter的實現(xiàn)以及成員變量契吉。
如果category中的方法和類中原有方法同名跳仿,運(yùn)行時會優(yōu)先調(diào)用category中的方法。也就是捐晶,category中的方法會覆蓋掉類中原有的方法菲语。所以開發(fā)中盡量保證不要讓分類中的方法和原有類中的方法名相同。避免出現(xiàn)這種情況的解決方案是給分類的方法名統(tǒng)一添加前綴惑灵。比如category_山上。
如果多個category中存在同名的方法,運(yùn)行時到底調(diào)用哪個方法由編譯器決定英支,最后一個參與編譯的方法會被調(diào)用佩憾。
3、調(diào)用優(yōu)先級
分類(category) > 本類 > 父類干花。即妄帘,優(yōu)先調(diào)用cateory中的方法,然后調(diào)用本類方法池凄,最后調(diào)用父類方法抡驼。
Category的底層結(jié)構(gòu)
-
定義在objc-runtime-new.h 中
struct category_t{ const char*name; classref_t cls; struct method_list_t*instanceMethods; struct method_list_t *classMethods; struct proptocol_list_t *protoclos; sturct property_list *instanceProperties; struct property_list_t *_classProperties; method_list_t *methodsForMeta(bool isMeta){ if(isMeta) return classMethods; else return instanceMethods; property_list_t *proertiesForMeta(bool isMeta,stuct header_info*hi); } }
Category加載過程
- 通過Runtime加載某個類所有的Category數(shù)據(jù)
- 把所有的Category 的方法、屬性肿仑、協(xié)議數(shù)據(jù)致盟,合并到一個大數(shù)組中。后面圖片中?后面參與編譯的Category數(shù)據(jù)柏副,回在數(shù)組的前面勾邦。
-
將合并的分類數(shù)據(jù)(方法、屬性割择、協(xié)議)眷篇,插入到類原來的數(shù)據(jù)前面。
E47E26A9-B4D9-4753-807E-62E8324FB25F.png
load 方法的調(diào)用
+load 方法會在runtime加載類荔泳、分類時調(diào)用蕉饼。
每個分類、類的load方法玛歌,在程序運(yùn)行中只調(diào)用一次
-
調(diào)用順序
1 先調(diào)用類的load方法
2 按照編譯先后順序調(diào)用(先編譯昧港,先調(diào)用)
3 調(diào)用子類的load 方法之前回先調(diào)用父類的+load 方法。分類
1 在調(diào)用分類的load方法
2 按照編譯先后順序調(diào)用先(先編譯支子,先調(diào)用)创肥。
+initizlize方法
+initialize 方法會在類的第一次接收到消息時調(diào)用。
調(diào)用順序
先調(diào)用父類的initzlize ,在調(diào)用子類的initialize
先初始化父類叹侄,在初始化子類巩搏,每個類只會初始化一次。initialize 和load 的最大區(qū)別
initialize時通過objc_msgSend進(jìn)行調(diào)用的
如果子類沒有實現(xiàn)initialize 會調(diào)用父類的initialize(所以父類的的initialize可能會被剁細(xì)調(diào)用)
如果分類實現(xiàn)了initizlize 就會覆蓋類本省的initialize調(diào)用
如何給分類“添加成員變量”
默認(rèn)情況下趾代,因為分類底層結(jié)構(gòu)的限制贯底,不能添加成員變量到分類中。但是可以通過關(guān)聯(lián)對象來說實現(xiàn)撒强。
-
關(guān)聯(lián)對象的API
添加關(guān)聯(lián)對象 void objc_setAssociatedObject(id object,const voie *key,id value,objc_associationPolicy policy) 獲取關(guān)聯(lián)對象 id objc_getAssociatedObject(id object,const void *key) 移除所有的關(guān)聯(lián)對象 void objc——removeAssociatedObjects(Id object)
-
key的常見用法
static void *MyKey = &Mykey; objc_setAssociatedObject(obj,MYkey,OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_getASSOciatedObjct(obj,Mykey); static chat Mykey; objc_setAssociatedObject(obj,&Mykey,value,OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_getAssociatedObject(obj,&Mykey); 使用屬性名作為key objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC); objc_getAssociatedObject(obj, @"property"); 使用get方法的@selecor作為key objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC) objc_getAssociatedObject(obj, @selector(getter))
關(guān)聯(lián)對象的原理
實現(xiàn)管來呢對象技術(shù)的核心對象有
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- ObjectAssociation