Category与Extension

1. Category

1.1 定义与特点

  • ObjC语言的一个灵活的类扩展机制,可以在不知道原有代码的基础上向一个已存在类添加新的方法
  • 不能修改、删除已有方法,对扩展开放、对修改封闭,体现了面向对象的OCP原则,降低了代码的耦合度。
  • 类别扩展的新方法有更高优先级,会覆盖类中已有的同名方法;声明的方法可以不实现,但不实现时不可以调用此方法

1.2 关于添加变量

  • 只能添加扩展方法,不能添加成员变量
  • 因为Category是运行期决议,运行时对象的内存布局已经确定,objc_class结构体大小已确定,无法再添加成员变量
  • Category中定义了property后,编译器只为其声明了存取方法,但没有实现;也没有合成加下划线的实例变量名,导致无法访问实例变量名

1.3 用runtime为属性添加存取方法

  1. 可以使用runtime提供的关联对象方法,动态的为property实现gettersetter,以实现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_COPYOBJC_ASSOCIATION_COPY:关联对象会被释放
  1. 扩展NSString类的方法为例,扩展属性、实例方法和类方法,使用runtime实现属性的gettersetter,代码如下:
//  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;
}
  • 运行结果如下

Category与Extension

2. Extension

2.1 位置

  • 在类的.m文件中@implementation之前的地方,即类的continuous区域

2.2 为啥要用

  • 类扩展的作用本来是私有方法的前向声明,但最新的编译器可以在.m文件中任意位置写实现而无需声明,如果声明了就一定要实现
  • 现在Extension区域的作用主要是定义类的私有属性,将暴露给外部的属性、变量和方法定义在.h文件中,不想暴露的直接定义在.m文件的Extension
  • 私有并不是绝对的,仍然可以使用KVCruntime暴力访问私有属性和变量、使用runtime暴力访问私有方法

2.2 关于添加变量

  • Extension是编译器决议的,它就是类的一部分,因此可以添加成员变量
  • Category运行期决议,不能添加成员变量
/*类扩展区域*/
@interface extensionClass()

/* 类扩展属性,类外部不可访问*/
@property(nonatomic, copy) NSString* extensionVariable;

/* 类扩展方法声明,可省略,若声明了就得在后面实现 */
-(void)extensionInstanceMethod;
+(void)extensionClassMethod;
上一篇:与众不同 windows phone (33) - Communication(通信)之源特定组播 SSM(Source Specific Multicast)


下一篇:Google插件开发探索