【iOS】Category、Extension和关联对象

Category分类

Category 是 比继承更为简洁 的方法来对Class进行扩展,无需创建子类就可以为现有的类动态添加方法。

  • 可以给项目内任何已经存在的类 添加 Category
  • 甚至可以是系统库/闭源库等只暴露了声明文件的类 添加 Category (看不到.m 文件的类)
  • 通过 Category 可以添加 实例方法、类方法、属性
    • 注意:通过 Category 可以添加属性,会声明settergetter方法 ,但需要 开发者 自己 实现 settergetter方法(使用关联对象实现属性)
    • 通过 Class 添加属性,会默认生成并实现 settergetter方法
  • 分类也可以把framework私有方法公开化
    • 比如我们假如我有一个类里有一个私有方法A 外界是调用不到的 但是我给这个类写了个category 在里里面申明了一个方法(也叫A,只申明,不实现) 现在我import这个category 调用 这个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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值