OC - Category 和 Extension

网络配图

1. Category 分类

1.1 Category 的使用场合

  • ① 给一个类添加新的方法,可以为系统的类扩展功能。
  • ② 分解体积庞大的类文件,可以将一个类按功能拆解成多个模块,方便代码管理。
  • ③ 创建对私有方法的前向引用:声明私有方法,把 Framework 的私有方法公开等。直接调用其他类的私有方法时编译器会报错的,这时候可以创建一个该类的分类,在分类中声明这些私有方法(不必提供方法实现),接着导入这个分类的头文件就可以正常调用这些私有方法。
  • ④ 向对象添加非正式协议:创建一个 NSObject 或其子类的分类称为 “创建一个非正式协议”。
    (正式协议是通过 protocol 指定的一系列方法的声明,然后由遵守该协议的类自己去实现这些方法。而非正式协议是通过给 NSObject 或其子类添加一个分类来实现。非正式协议已经渐渐被正式协议取代,正式协议最大的优点就是可以使用泛型约束,而非正式协议不可以。)

1.2 Category 中都可以添加哪些内容?

  • 实例方法、类方法、协议、属性(只生成 setter 和 getter 方法的声明,不会生成 setter 和 getter 方法的实现以及下划线成员变量);
  • 默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中,但可以通过关联对象来间接实现这种效果。

1.3 Category 的优缺点、特点、注意点

  • 优点:
    ① 见 Category 的使用场合;
    ② 可以按需加载不同的分类。
  • 缺点:
    ① 不能直接添加成员变量,但可以通过关联对象实现这种效果;
    ② 分类方法会“覆盖”同名的宿主类方法,如果使用不当会造成问题。
  • 特点:
    ① 运行时决议
    ② 可以有声明,可以有实现
    ③ 可以为系统的类添加分类
    (运行时决议:Category 编译之后的底层结构时struct category_t,里面存储着分类的对象方法、类方法、属性、协议信息,这时候分类中的数据还没有合并到类中,而是在程序运行的时候通过Runtime机制将所有分类数据合并到类(类对象、元类对象)中去。(这是分类最大的特点,也是分类和扩展的最大区别,扩展是在编译的时候就将所有数据都合并到类中去了)
  • 注意点:
    ① 分类方法会“覆盖”同名的宿主类方法,如果使用不当会造成问题;
    ② 同名分类方法谁能生效取决于编译顺序,最后参与编译的分类中的同名方法会最终生效;
    ③ 名字相同的分类会引起编译报错。

1.4 Category 的实现原理

  • ① 分类的实现原理取决于运行时决议;
  • ② 同名分类方法谁能生效取决于编译顺序,最后参与编译的分类中的同名方法会最终生效;
  • ③ 分类方法会“覆盖”同名的宿主类(原类)方法,这里说的“覆盖”并不是指原来的方法没了。消息传递过程中优先查找宿主类中靠前的元素,找到同名方法就进行调用,但实际上宿主类中原有同名方法的实现仍然是存在的。我们可以通过一些手段来调用到宿主类原有同名方法的实现,如可以通过Runtimeclass_copyMethodList方法打印类的方法列表,找到宿主类方法的imp,进行调用(可以交换方法实现)。
1.4.1 编译
源码分析

通过 Clang 将以下分类代码转换为 C++ 代码,来分析分类的底层实现。

// Clang
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test.m
#import "Person.h"
@interface Person (Test)<NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
- (void)eat;
- (void)sleep;
+ (void)run;
+ (void)walk;
@end

#import "Person+Test.h"
@implementation Person (Test)
- (void)eat {
   
    NSLog(@"eat");
}
- (void)sleep {
   
    NSLog(@"sleep");
}
+ (void)run {
   
    NSLog(@"run");
}
+ (void)walk {
   
    NSLog(@"walk");
}
@end
// Person+Test.cpp
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;
};

// 实例方法列表
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_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
   
	sizeof(_objc_method),
	2,
	{
   {
   (struct objc_selector *)"eat", "v16@0:8", (void *)_I_Person_Test_eat},
	
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值