Category分类
Category
是 比继承更为简洁 的方法来对Class
进行扩展,无需创建子类就可以为现有的类动态添加方法。
- 可以给项目内任何已经存在的类 添加
Category
- 甚至可以是系统库/闭源库等只暴露了声明文件的类 添加
Category
(看不到.m
文件的类) - 通过
Category
可以添加 实例方法、类方法、属性- 注意:通过
Category
可以添加属性,会声明setter
、getter
方法 ,但需要 开发者 自己 实现setter
、getter
方法(使用关联对象实现属性) - 通过
Class
添加属性,会默认生成并实现setter
、getter
方法
- 注意:通过
- 分类也可以把
framework
私有方法公开化- 比如我们假如我有一个类里有一个私有方法A 外界是调用不到的 但是我给这个类写了个
category
在里里面申明了一个方法(也叫A,只申明,不实现) 现在我import
这个category
调用 这个A 的情况是怎么样的呢?实际上这时候就会调用私有方法这个A,我们通过分类将私有方法公开化了
- 比如我们假如我有一个类里有一个私有方法A 外界是调用不到的 但是我给这个类写了个
- 通过
Category
可以 重新实现 在Class
中已存在的 方法 - 通过
Category
可以 重新实现 在 其它Category
中已存在/已实现 的方法
[ 在iOS
中,实例对象/类对象方法调用顺序严格依赖 源码文件的编译顺序,编译顺序的查看可以通过Xcode>Build Phases>Compile Sources
查看:
- 类与 各个分类 各自声明且实现各自的方法:没有方法的实现被覆盖,分类 只是扩展了 类的 功能
- 类与 各个分类 存在 声明 且实现 了同名的 方法: 存在 方法的实现被覆盖(实际上不是被覆盖,而是方法地址后挪,系统会找到同名方法在内存地址中位置较前的方法 实现 调用)
- 分类 方法实现 的优先级 > 原来的类
- 各个分类 中 被覆盖的情况严格 依赖 源码 文件的编译顺序:
- 先编译的 方法 会 先加入 方法列表「先入栈」
- 后编译的 方法 会 后加入 方法列表「后入栈」
- 系统在调用 方法 的实现的时候,通过 对象(实例对象、类对象等) 和 方法API 在底层发送消息,拿到方法 实现 的 实现 IMP指针 找到 方法的具体实现(实际上最终拿到的方法实现,是后编译的源码文件中的方法实现)
官方介绍的优点有两点:
- 可以把类的实现分开在几个不同的文件里面
- 可以减少分开文件的体积
- 可以把不同的功能组织到不同的category里
- 可以有多个开发者共同完成一个类
- 可以按需加载想要的类别等等
- 声明专有方法
Extension扩展
延展(Extension
)可以理解成是匿名的Category
可以用来给类 添加 属性和方法 的声明,不作用在Subclass
可以 通过 在.m
文件 给其它类 或者 当前 类 添加 Extension
给当前类添加 Extension
时,编译器会默认给 添加的属性 声明且实现 其setter&&getter
方法
也可以 通过 .h
文件 给类 添加 Extension
要对 Extension
添加的 属性和方法进行实现
若 对 Extension
的实现中 ,重新实现 原来 类或其它分类中已有的方法,不会对原来的方法执行产生影响(因为没有自身的.m
文件,不存在源码实现文件的编译的情况)
Extension
的作用更多在于拆分结构复杂的类,比较清晰的暴露声明
Category的实质
Category结构体
typedef struct category_t *Category;
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);
};
分类的结构体中可以为类添加对象方法、类方法、协议、属性,但是并没有成员变量
将分类转成C++看起
接着我们将分类的.m文件转成C++文件来了解一下:
我们首先先创建一个分类:
#import "Car.h"
#import "protocolForCar.h"
NS_ASSUME_NONNULL_BEGIN
@interface Car (match)<CarProtocol>
@property (nonatomic, copy) NSString *carType;
- (void)matchPrint;
+ (void)matchClass;
@end
NS_ASSUME_NONNULL_END
我们在其中声明了一个实例方法、一个类方法、一个属性
分类遵循一个协议,协议里面也是一个类方法和对象方法
#ifndef protocolForCar_h
#define protocolForCar_h
@protocol CarProtocol <NSObject>
- (void)protocolMethod;
+ (void)protocolClassMethod;
@end
然后使用:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Car+match.m -o test.cpp
,将该分类的.m文件转为C++文件:
//category结构体
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;
};
//category结构体赋值
static struct _category_t _OBJC_$_CATEGORY_Car_$_match __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Car",
0, // &OBJC_CLASS_$_Car,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Car_$_match,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Car_$_match,
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Car_$_match,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Car_$_match,
};
//结构体数组
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_Car_$_match,
};
我们可以看到重点的三个元素
- category结构体
- category结构体的赋值语句
- category结构体数组
对象方法列表结构体
//本类对象方法的实现
static void _I_Car_match_matchPrint(Car * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_0);
}
//协议中对象方法的实现
static void _I_Car_match_protocolMethod(Car * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_2);
}
//对象方法列表结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Car_$_match __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{
{
(struct objc_selector *)"matchPrint", "v16@0:8", (void *)_I_Car_match_matchPrint},
{
(struct objc_selector *)"protocolMethod", "v16@0:8", (void *)_I_Car_match_protocolMethod}}
};
- (void)matchPrint
和- (void)protocolMethod
方法的实现- 对象方法结构体列表结构体
只要是在Category
中实现了的对象方法(包括代理中的对象方法)。都会添加到对象方法列表结构体_OBJC_$_CATEGORY_INSTANCE_METHODS_Car_$_match
中来,如果仅仅是定义,没有实现,不会加进来
类方法列表结构体
//本类类方法的实现
static void _C_Car_match_matchClass(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_1);
}
//协议中的类方法
static void _C_Car_match_protocolClassMethod(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_6p_mn3hwpz14_7dg_gr79rtm4n80000gn_T_Car_match_633f34_mi_3);
}
//类方法列表结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2];
} _OBJC_$_CATEGORY_CLASS_METHODS_Car_$_match __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{
{
(struct objc_selector *)"matchClass", "v16@0:8", (void *)_C_Car_match_matchClass},
{
(struct objc_selector *)"protocolClassMethod", "v16@0:8", (void *)_C_Car_match_protocolClassMethod}}
};
+ (void)matchClass
和+ (void)protocolClassMethod
类方法的实现- 类方法列表结构体
只要是Category
中实现了的类方法(包括代理中的类方法)。都会添加到类方法列表结构体_OBJC_$_CATEGORY_CLASS_METHODS_Car_$_match
中来
协议列表结构体
//协议结构体
struct _protocol_t {
void * isa; // NULL
const char *protocol_name;
const struct _protocol_list_t * protocol_list; // super protocols
const struct method_list_t *instance_methods;
const struct method_list_t *class_methods;
const struct method_list_t *optionalInstanceMethods;
const struct method_list_t *optionalClassMethods;
const struct _prop_list_t * properties;
const unsigned int size; // sizeof(struct _protocol_t)
const unsigned int flags; // = 0
const char ** extendedMethodTypes;
};
//分类中添加的协议列表结构体
static struct /*_protocol_list_t*/ {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1];
} _OBJC_PROTOCOL_REFS_CarProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_NSObject
};
//协议列表 对象方法列表结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_PROTOCOL_INSTANCE_METHODS_CarProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{
{
(struct objc_selector *)"protocolMethod", "v16@0:8", 0}}
};
//协议列表 类方法列表结构体
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_PROTOCOL_CLASS_METHODS_CarProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{
{
(struct objc_selector *)"protocolClassMethod", "v16@0:8", 0}}
};
//结构体赋值
struct _protocol_t _OBJC_PROTOCOL_CarProtocol __attribute__ ((used)) = {
0