一: category
category是Objective-C 2.0之后添加的語言特性,別人口中的 分類、類別其實都是指的category。category的主要作用是為已經(jīng)存在的類添加方法。除此之外,apple還推薦了category的另外兩個使用場景。
1.1: 什么是Category
可以把類的實現(xiàn)分開在幾個不同的文件里面。這樣做有幾個顯而易見的好處。
- 可以減少單個文件的體積
- 可以把不同的功能組織到不同的category里
- 可以由多個開發(fā)者共同完成一個類
- 可以按需加載想要的category
- 聲明私有方法
apple的SDK中就大面積的使用了category這一特性。比如UIKit中的UIView。apple把不同的功能API進行了分類,這些分類包括UIViewGeometry、UIViewHierarchy、UIViewRendering等。
不過除了apple推薦的使用場景,廣大開發(fā)者腦洞大開,還衍生出了category的其他幾個使用場景:
- 模擬多繼承(另外可以模擬多繼承的還有protocol)
- 把framework的私有方法公開
1.2: category特點
-
category只能給某個已有的類擴充方法,不能擴充成員變量。 -
category中也可以添加屬性,只不過@property只會生成setter和getter的聲明,不會生成setter和getter的實現(xiàn)以及成員變量。 - 如果
category中的方法和類中原有方法同名,運行時會優(yōu)先調(diào)用category中的方法。也就是,category中的方法會覆蓋掉類中原有的方法。所以開發(fā)中盡量保證不要讓分類中的方法和原有類中的方法名相同。避免出現(xiàn)這種情況的解決方案是給分類的方法名統(tǒng)一添加前綴。比如category_。 - 如果多個
category中存在同名的方法,運行時到底調(diào)用哪個方法由編譯器決定,最后一個參與編譯的方法會被調(diào)用。
如下圖,給UIView添加了兩個category(one 和 two),并且給這兩個分類都添加了名為log的方法:


在viewController中引入這兩個category的.h文件。調(diào)用log方法:

當(dāng)編譯順序如下圖所示時,調(diào)用UIView + one.m的log方法,如下圖:


將
UIView + one.m移動到UIView + two.m上面,調(diào)用UIView + two.m的log方法,如下圖:


1.3: 調(diào)用優(yōu)先級
分類(category) > 本類 > 父類。即,優(yōu)先調(diào)用cateory中的方法,然后調(diào)用本類方法,最后調(diào)用父類方法。
注意:category是在運行時加載的,不是在編譯時。
1.4: 為什么category不能添加成員變量?
Objective-C類是由Class類型來表示的,它實際上是一個指向objc_class結(jié)構(gòu)體的指針。它的定義如下:
typedef struct objc_class *Class;
objc_class結(jié)構(gòu)體的定義如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認為0
long info OBJC2_UNAVAILABLE; // 類信息,供運行期使用的一些位標(biāo)識
long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
在上面的objc_class結(jié)構(gòu)體中,ivars是objc_ivar_list(成員變量列表)指針;methodLists是指向objc_method_list指針的指針。在Runtime中,objc_class結(jié)構(gòu)體大小是固定的,不可能往這個結(jié)構(gòu)體中添加數(shù)據(jù),只能修改。所以ivars指向的是一個固定區(qū)域,只能修改成員變量值,不能增加成員變量個數(shù)。methodList是一個二維數(shù)組,所以可以修改*methodLists的值來增加成員方法,雖沒辦法擴展methodLists指向的內(nèi)存區(qū)域,卻可以改變這個內(nèi)存區(qū)域的值(存儲的是指針)。因此,可以動態(tài)添加方法,不能添加成員變量。