Category 是基于 Objective-C runtime 的一種體現(xiàn)坑傅。
Category 原理
首先呜呐,這里先給下 runtime 源碼的下載地方战得,然后我們來看下 Class 的結(jié)構(gòu)體:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
可以看出是個(gè) objc_class 類型的結(jié)構(gòu)體充边。他包含:
isa:一個(gè) isa 指針,實(shí)例的 isa 指針指向他的類常侦,類的 isa 指針指向他的元類浇冰,元類 isa 指針指向他的根元類,根元類的 isa 指針指向他本身聋亡。這樣就形成了一個(gè)閉環(huán)肘习;
super_class:父類;
name:類的名字坡倔;
version:版本信息漂佩,默認(rèn)為0,當(dāng)然也可以在運(yùn)行時(shí)通過方法 class_setVersion 修改版本信息和通過 class_getVersion 來獲取版本信息罪塔;
info:供運(yùn)行時(shí)使用的一些標(biāo)識(shí)投蝉,一些位掩碼;
instance_size:實(shí)例變量的大小征堪,包括繼承父類的瘩缆;
objc_ivars_list:實(shí)例變量集合;
objc_method_list:方法集合佃蚜;
objc_cache:方法緩存庸娱;
objc_protocol_list:協(xié)議集合。
在看看 category 結(jié)構(gòu):
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;
};
name:類名稱谐算;
cls:類的對(duì)象熟尉;
instance_methods:實(shí)例方法集合;
class_methods:類方法集合氯夷;
protocols:協(xié)議集合臣樱;
properties:屬性集合。不過這個(gè) property 不會(huì) @synthesize 實(shí)例變量腮考。
Category 的實(shí)用場(chǎng)景
可以看出雇毫,class 和 category 的內(nèi)部結(jié)構(gòu)很類似的,所以他是幫助 calss 來完成一些便捷操作的:
- 為不能直接修改實(shí)現(xiàn)文件的類修改方法踩蔚,因?yàn)?category 的優(yōu)先級(jí)要高于類自生實(shí)現(xiàn)的方法棚放;
- 在不修改類的實(shí)現(xiàn)添加方法;
- 分類管理類的方法馅闽、協(xié)議等飘蚯,更于清晰類的目錄馍迄;
- 動(dòng)態(tài)添加屬性。
Category 實(shí)現(xiàn)內(nèi)部
我們?cè)谶@給 Animal 類添加一個(gè) Behavior 的 Category 局骤,添加一個(gè) eat 方法攀圈。
@interface Animal (Behavior)
- (void)eat;
@end
然后打開終端,cd 到該文件目錄下峦甩。使用命令:
clang -rewrite-objc Animal+Behavior.m
可以得到一個(gè) Animal+Behavior.cpp 文件赘来,他是個(gè) c++ 的文件,記錄了 Animal+Behavior.m 編譯過程凯傲,很多的內(nèi)容犬辰,不過大多數(shù) import 的東西,我們到文末最后看冰单,可以找到
static struct _category_t _OBJC_$_CATEGORY_Animal_$_Behavior __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Animal",
0, // &OBJC_CLASS_$_Animal,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Animal_$_Behavior,
0,
0,
0,
};
可以看到稍微符號(hào)化了一些幌缝。基本能看懂诫欠,看第三行涵卵,這個(gè)應(yīng)該放的 instance_methods』牡穑看到指向了
_OBJC_$_CATEGORY_INSTANCE_METHODS_Animal_$_Behavior
再看下他的實(shí)現(xiàn)
_OBJC_$_CATEGORY_INSTANCE_METHODS_Animal_$_Behavior __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"eat", "v16@0:8", (void *)_I_Animal_Behavior_eat}}
};
內(nèi)部可以看到剛才添加的 eat 方法缘厢。然后我們?cè)倏匆粋€(gè)
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_Animal_$_Behavior,
};
這就是該 category 的數(shù)組集合了,從這幾個(gè)編譯過程就大概可以看明白 category 了甩挫。
Category 的實(shí)用
最常用的莫過于添加方法和變量贴硫,方法比較簡(jiǎn)單,而前面提到的 properties 不會(huì)實(shí)現(xiàn)@synthesize伊者,所有英遭,要用的運(yùn)行時(shí),動(dòng)態(tài)添加亦渗。
我們給 Animal+Behavior 添加一個(gè) legs 的屬性
@interface Animal (Behavior)
@property (nonatomic, assign) NSUInteger legs;
@end
@implementation Animal (Behavior)
- (void)setLegs:(NSUInteger)legs{
objc_setAssociatedObject(self, @selector(legs), @(legs), OBJC_ASSOCIATION_ASSIGN);
}
- (NSUInteger)legs{
return [objc_getAssociatedObject(self, _cmd) integerValue];
}
@end
這里使用運(yùn)行時(shí)的 objc_setAssociatedObject 和 objc_getAssociatedObject 挖诸,然后 (_cmd) 就代表代簽方法的 selector,然后在 set 方法有個(gè)屬性為 objc_AssociationPolicy:
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
這個(gè)易懂法精,和修飾 property 修飾詞類似多律。這樣就動(dòng)態(tài)添加了一個(gè) legs 屬性。
小結(jié)
熟練及合適的實(shí)用 Category 能為我們開發(fā)中帶來很多的便利搂蜓,理解知曉結(jié)構(gòu)原理更能盡快的解決我們?cè)陂_發(fā)中遇到的相關(guān)問題狼荞。