Runtime详解(三)Category底层原理

本文详细探讨了Objective-C的Category特性,包括它的用途、与Extension的区别,以及为何不能添加成员变量。讲解了Category的结构体、C++源码解析、加载过程和方法覆盖机制,并介绍了Category如何与类的+load方法交互。此外,还讨论了如何在Category中使用关联对象实现属性的getter和setter方法。
摘要由CSDN通过智能技术生成

Category(分类)简介

Category(分类)是Objective-C 2.0 添加的语言特性,主要作用是为已经存在的类添加分类。Category可以做到既不子类化,也不侵入一个类的源码的情况下,为原有的类添加新的方法,从而实现扩展一个类或者分离一个类的目的。在日常开发中我们常常使用Category为已有的类扩展功能。

虽然继承也能为已有类增加新的方法,而且还能直接增加属性,但继承关系增加了不必要的代码复杂度,在运行时,也无法与父类的原始方法进行区分。所以我们可以优先考虑使用自定义Category(分类)。

通常Category(分类)有以下几种使用场景:

把类的不同实现方法分开到不同的文件里。

声明私有方法

模拟多继承

将framework私有方法公开化

Category(分类)和Extension(扩展)

Category (分类)看起来和Extension(扩展)有点相似。Extension (扩展)有时候也称为匿名分类。但两者实质上是不同的东西。Extension(扩展)是在编译阶段与该类同时编译的,是类的一部分,而且Extension(扩展)中声明的方法只能在该类同时编译的@implementation 中实现,这也就意味着,你无法对系统的类(例如 NSString 类)使用Extension(扩展)。而且和Category(分类)不同的是,Extension(扩展)不但可以声明方法,还可以声明成员变量,这是Category(分类)所做不到的。

(为什么Category(分类)不能像 Extension(扩展)一样添加成员变量?

​ 因为Extension(扩展)是在编译阶段与该类同时编译的,就是类的一部分。既然作为类的一部分,且与类同时编译,那么就可以在编译阶段为类添加成员变量。而Category(分类)不同,Category(分类)的特性是:可以在运行时阶段动态地为已有类添加新行为。Category(分类)是在运行时期间决定的。而成员变量的内存布局已经在编译阶段确定好了,如果在运行阶段添加成员变量的话,就会破坏原有类的内存布局,从而造成可怕的后果,所以Category(分类)无法添加成员变量。 )

Category的实质

Category结构体简介

我们之前知道了Object (对象)和 Class(类)的实质分别是objc_object结构体和objc_class结构体,这里Category也不例外,在objc-runtime-new.h 中,Category(分类)被定义为 category_t 结构体。category_t 结构体的数据结构如下:

typedef struct category_t *Category;

struct category_t {
   
  	const char *name; // 类名
  	classref_t cls; // 类,在运行时阶段通过 clasee_name(类名)对应到类对象
  	struct method_list_t *instanceMethods; // Category 中所有添加的对象方法列表
  	struct method_list_t *classMethods; // Category 中所有添加的类方法列表
  	struct protocol_list_t *protocols; // Category 中实现的所有协议列表
  	struct property_list_t *instanceProperties; // Category 中添加的所有属性
};

从Category(分类)的结构体定义中也可以看出,Category(分类)可以为类添加对象方法、类方法、协议、属性。同时,也能发现Category(分类)无法添加成员变量。

Category的C++源码

想要了解Category的本质,我们需要借助于Category的C++源码。首先呢,我们需要写一个继承自NSObject的Person类,还需要写一个Person+Additon的分类。在分类中添加对象方法,类方法,属性,以及代理。

/***********************Person+Addition.h 文件****************************/

#import "Person.h"

// PersonProtocol 代理
@protocol PersonProtocol <NSObject>
  
  - (void)PersonProtocolMethod;

	+ (void)PersonProtocolClassMethod;

@end
  
@interface Person (Addition) <PersonProtocol>
  
  /* name 属性*/
  @property (nonatomic, copy) NSString *personName;

	// 类方法
	+ (void)printClassName;

	// 对象方法
	- (void)printName;
  
@end
  
 /***********************Person+Addition.m 文件****************************/
 #import "Person+Addition.h"
  
 @implementaion Person (Addition)
  
  + (void)printClassName {
  	NSLog(@"printClassName");
	}
	
	- (void)printName {
    NSLog(@"printName");
  }

#pragma mark - <PersonProtocol> 方法

- (void)PersonProtocolMethod {
  	NSLog(@"PersonProtocolMethod");
}

+ (void)PersonProtocolClassMethod {
  	NSLog(@"PersonProtocolClassMethod");
}

@end

Category 由 OC 转 C++ 源码方法如下:

1.在项目中添加Person 类文件 Persion.h 和 Person.m, Person 类继承自NSObject.
2.在项目中添加Person类的Category文件Person+Addition.h和Person+Addition.m,并在Category中添加相关对象方法,类方法,属性,以及代理。
3.打开『终端』,执行 cd XXX/XXX 命令,其中 XXX/XXX 为 Category 文件 所在的目录。
4.继续在终端执行 clang -rewrite-objc Person+Addition.m 
5.执行完命令之后,Person+Addition.m 所在目录下就会生成一个 Person+Addition.cpp 文件,这就是我们需要的 Category(分类) 相关的 C++ 源码。

得到Person+Addition.cpp文件之后,就会神奇的发现:这是一个3.7M大小,拥有近10W行代码的庞大文件。

Category的相关C++源码在文件的底部,删除其他无关代码,会剩

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值