1. Category
1.1 定义与特点
- ObjC语言的一个灵活的类扩展机制,可以在不知道原有代码的基础上向一个已存在类添加新的方法
- 不能修改、删除已有方法,对扩展开放、对修改封闭,体现了面向对象的OCP原则,降低了代码的耦合度。
- 类别扩展的新方法有更高优先级,会覆盖类中已有的同名方法;声明的方法可以不实现,但不实现时不可以调用此方法
1.2 关于添加变量
- 只能添加扩展方法,不能添加成员变量
- 因为
Category
是运行期决议,运行时对象的内存布局已经确定,objc_class
结构体大小已确定,无法再添加成员变量
- 在
Category
中定义了property
后,编译器只为其声明了存取方法,但没有实现;也没有合成加下划线的实例变量名,导致无法访问实例变量名
1.3 用runtime为属性添加存取方法
- 可以使用
runtime
提供的关联对象方法,动态的为property
实现getter
和setter
,以实现setter
为例
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
- key是一个void指针
- value是关联对象,是
id
类型(编译时类型不确定,视为任意Object类型)
- policy指定一个内存管理策略来处理关联对象,
OBJC_ASSOCIATION_ASSIGN
:被关联对象释放时,关联对象不会被释放
OBJC_ASSOCIATION_COPY
、OBJC_ASSOCIATION_COPY
:关联对象会被释放
- 扩展
NSString
类的方法为例,扩展属性、实例方法和类方法,使用runtime实现属性的getter
和setter
,代码如下:
// NSString+Category.h
#import <Foundation/Foundation.h>
@interface NSString (Category) {
/*不可添加成员变量*/
}
@property(nonatomic,copy) NSString *newString;
//扩展的实例方法和类方法
+(void)categoryClassMethod;
-(void)categoryInstanceMethod;
@end
// NSString+Category.m
#import "NSString+Category.h"
#import <objc/runtime.h>
@implementation NSString (Category)
//runtime强行实现newString的getter和setter
-(NSString *)newString {
return objc_getAssociatedObject(self, @"newString");
}
-(void)setNewString:(NSString *)newString {
objc_setAssociatedObject(self, @"newString", newString, OBJC_ASSOCIATION_COPY);
}
//扩展的实例方法和类方法
+(void)categoryClassMethod {
NSLog(@"这是扩展的类方法");
}
-(void)categoryInstanceMethod {
NSLog(@"这是扩展的实例方法");
}
@end
// main.m
#import <Foundation/Foundation.h>
#import "NSString+Category.h"
int main(int argc, const char * argv[]) {
NSString *string = [[NSString alloc] init];
//runtime实现的getter和setter
string.newString = @"你好";
NSLog(@"%@",string.newString);
//扩展的类方法和实例方法
[NSString categoryClassMethod];
[string categoryInstanceMethod];
return 0;
}
2. Extension
2.1 位置
- 在类的.m文件中
@implementation
之前的地方,即类的continuous
区域
2.2 为啥要用
- 类扩展的作用本来是私有方法的前向声明,但最新的编译器可以在.m文件中任意位置写实现而无需声明,如果声明了就一定要实现
- 现在
Extension
区域的作用主要是定义类的私有属性,将暴露给外部的属性、变量和方法定义在.h文件中,不想暴露的直接定义在.m文件的Extension
区
- 私有并不是绝对的,仍然可以使用
KVC
和runtime
暴力访问私有属性和变量、使用runtime
暴力访问私有方法
2.2 关于添加变量
- Extension是编译器决议的,它就是类的一部分,因此可以添加成员变量
- Category运行期决议,不能添加成员变量
/*类扩展区域*/
@interface extensionClass()
/* 类扩展属性,类外部不可访问*/
@property(nonatomic, copy) NSString* extensionVariable;
/* 类扩展方法声明,可省略,若声明了就得在后面实现 */
-(void)extensionInstanceMethod;
+(void)extensionClassMethod;