前言
引用 iOS分類(category),類擴展(extension)—史上最全攻略
分類 Category
概念
分類 Catgegory
是 OC
中特有的語法,他表示一個指向分類的結構體指針。原則上,分類只能增加方法,不能添加變量。但是可以通過 runtime
實現變量的添加。
源碼
runtime.h
中實現:
Category
Category 是表示一個指向分類的結構體的指針,其定義如下:
typedef struct objc_category *Category;
struct objc_category {
char *category_name OBJC2_UNAVAILABLE; // 分類名
char *class_name OBJC2_UNAVAILABLE; // 分類所屬的類名
struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; // 實例方法列表
struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // 類方法列表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 分類所實現的協議列表
}
objc-runtime-new.h
中實現:
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;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t *methodsForMeta(bool isMeta) {
if (isMeta) return classMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
以上,可以看出,分類中既有實例方法、類方法的存儲列表,也有實例屬性、類屬性的列表。但是變量列表僅僅定義了,并未做返回設置。個人猜測,這也是為啥么添加成員變量,訪問會崩潰的原因
注意:
- 分類中無法添加成員變量,即下劃線變量。
- 分類中可以寫
@property
屬性,但是不會生成默認的setter
、getter
方法,也不會生成實現,以及私有的成員變量,編譯時警告。 - 可以在分類中訪問主類
.h
中的屬性、方法,但是無法訪問下劃線屬性。 - 如果分類中右主類同名方法,調用時優先執行分類中的方法。并不是覆蓋,以為主類中的方法依然在,只是查找到指定的方法后,就不再繼續查找了。方法調用順序
分類
>主類
>父類
。 - 如果多個分類都有同名方法,則執行順序由編譯器決定。
分類格式
@interface 待擴展的類(分類的名稱)
@end
@implementation 待擴展的名稱(分類的名稱)
@end
舉例:
// Programmer+Category.h文件中
@interface Programmer (Category)
@property(nonatomic,copy) NSString *nameWithSetterGetter; //設置setter/getter方法的屬性
@property(nonatomic,copy) NSString *nameWithoutSetterGetter; //不設置setter/getter方法的屬性(注意是可以寫在這,而且編譯只會報警告,運行不報錯)
- (void) programCategoryMethod; //分類方法
@end
// Programmer+Category.m文件中
下面通過 runtime
實現分類添加屬性的功能:
#import <objc/runtime.h>
static NSString *nameWithSetterGetterKey = @"nameWithSetterGetterKey"; //定義一個key值
@implementation Programmer (Category)
//運行時實現setter方法
- (void)setNameWithSetterGetter:(NSString *)nameWithSetterGetter {
objc_setAssociatedObject(self, &nameWithSetterGetterKey, nameWithSetterGetter, OBJC_ASSOCIATION_COPY);
}
//運行時實現getter方法
- (NSString *)nameWithSetterGetter {
return objc_getAssociatedObject(self, &nameWithSetterGetterKey);
}
@end
類擴展 Class Extension
Extension
是 Category
的一個特例。類擴展因為不包含分類名稱,又稱 匿名分類
。
開發中,我們幾乎天天在使用類擴展。
類擴展格式:
@interface XXX ()
//私有屬性
//私有方法(如果不實現,編譯時會報警,Method definition for 'XXX' not found)
@end
類擴展作用:
- 為一個類添加原來沒有的變量、方法和屬性
- 一般來說,類擴展寫到
.m
文件中 - 一般私有屬性寫到
.m
文件的類擴展中
分類 VS 擴展
- 分類中原則上只能添加方法。可通過
runtime
添加屬性不能添加成員變量。 - 類擴展可以添加屬性、方法、成員變量。但默認是
private
類型的。 - 分類中,如果方法、屬性沒有實現,會有警告。擴展則不會有警告。因為。擴展是在編譯階段加到類中,而類別則是運行時加到類中。
- 擴展沒有單獨實現的部分,即
@implementation
。需要依賴主類.m
文件實現定義的功能。 -
.h
中類擴展方法、屬性是對外共有的,.m
中類擴展中聲明的,都是私有的。