Category与Extension

翟宾实
2023-12-01

Category

1、Category的背景和概念

在日常开发中,经常需要对已有类进行功能上的扩展,在学习“类别”之前,常用的类扩展方式有以下三种:

  1. 原有类的修改

  2. 继承

  3. protocol(协议)

针对以上三种扩展方式,

  • 第一种;

  • 第二种在继承父类的同时,也扩展自己,包括(方法和变量)。但是在面向对象的开发原则中“优先使用组合慎用继承”,因为继承在一定程度上破坏了封装性、子类随父类变动。

  • 第三种协议,主要是依靠实现类的具体方法实现,当扩展功能时,需要修改原有类,协议定义过多,实现类过于庞大。

那么,在oc中,当我们想避免上述两种扩展方式的缺点,又想只对现有类进行扩展些方法,并且不用去修改原有类以及使用它的地方的代码,就用到了Category(类别)。

类别是OC的特有语法,可以通过在类上声明和实现方法来扩展现有类的功能。原则上只能增加方法,不能增加成员变量。

2、category的声明及实现

@interface 需扩展的类 (类别的名称)
-(void)appendMethod;
@end

@implementation 需扩展的类 (类别的名称)
-(void)appendMethod{
}
@end

3、category的使用

如果需要扩展一个类,定义好此类的category ,则可以通过高该类的对象直接调用category中的扩展方法。(可以扩展类方法),同时category中也可以访问原有类.h中的属性

#import "pson.h"

@implementation pson

-(void) run{
    NSLog(@"run");
}
+(void) jump{
    NSLog(@"jump");
}
@end

 

#import "pson+eat.h"

@implementation pson (eat)
-(void) eat{
    [self number];
    NSLog(@"eat");
}
+(void) drink{
    NSLog(@"drink");
}
@end
//在main方法中
pson *me = [[pson alloc] init];
        [me run];
        [me eat];
  

4、category扩展属性(变量)

由于category本质上是个指向类型的结构体指针,在结构体中只有方法的列表,没有属性的列表,所以理论上只能增加方法不能增加属性。

如何通过category来扩展属性呢?

无法添加属性的根本原因是:在category中@property声明属性,系统不会生成_成员变量和setter、getter。

解决方法:手动添加setter和getter方法,采用的就是:关联引用(objc_setAssociatedObject和objc_getAssociatedObject)

其中,

objc_setAssociatedObject,接收4个参数:想关联到数据的对象、获取数据的键值、存储引用的值、关联的策略;

objc_getAssociatedObject,接收2个参数:关联到数据的对象、键值

关联策略

OBJC_ASSOCIATION_ASSIGN

指定值将被简单赋值、没有保留和释放

OBJC_ASSOCIATION_RETAIN_NONATOMIC

指定值通过非线程安全的方式赋值并保留

OBJC_ASSOCIATION_COPY_NONATOMIC

指定值通过非线程安全的方式复制

OBJC_ASSOCIATION_RETAIN

指定值通过线程安全的方式赋值并保留

OBJC_ASSOCIATION_COPY

指定值通过线程安全的方式复制

#import "pson+eat.h"
#import "objc/runtime.h"
static NSString *key = @"personEatKey";

@implementation pson (eat)
-(void) eat{
    NSLog(@"eat");
}

-(void) setFood:(NSString *)food{
    objc_setAssociatedObject(self, &key, food, OBJC_ASSOCIATION_COPY);
}

-(NSString *) food{
    return objc_getAssociatedObject(self, &key);
}
@end
#import <Foundation/Foundation.h>
#import "pson.h"
#import "pson+eat.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {

        pson *me = [[pson alloc] init];
        [me eat];
        me.food = @"鸡蛋";
        NSLog(@"%@",me.food);
        
    }
    return 0;
}

Extension

extension 是Category的特例,少了类别的名称,是匿名分类。声明私有方法和属性的机制。具体实现在原有类的.m文件中。

1、extension的格式

@interface XXX ()
//私有属性
//私有方法(如果不实现,编译时会报警,Method definition for 'XXX' not found)
@end

2、extension的实现

  1. 通过单独的.h声明,在原有类的.m中导入。

  2. 直接在原有类的.m中使用。

 

category和Extension的区别

  1. category原则上只能增加方法;而Extension方法和变量都可以。

  2. Extension声明方法没有被实现,编译器报警。category在运行时添加、Extension是编译阶段。

  3. Extension没有自己独立的实现部分。

  4. Extension是私有。

 类似资料: