【iOS】——类别与扩展

一、类别

类别又称为扩展类,在类的原基础上扩展方法,且不可添加变量,如果扩展的方法与原始类中的方法相同,则会隐藏原始方法,且不可在扩展方法中通过super调用原始方法, 这里与继承不同。

Objective-C的动态特征允许使用类别为现有的类添加新方法,并且不需要创建子类,不需要访问原有类的源代码
通过使用类别,我们可以动态地为现有的添加新方法,而且可以将类定义模块化地分配到多个相关文件中

类别同样由接口和实现部分组成,接口部分的语法格式如下:

@interface 已有类 (类别名)
//方法定义
...
@end

虽然这个语法格式看上去很像在定义类,但在类名后有一个圆括号,而且圆括号中带一个类别名。
定义类别的语法与定义类的语法存在如下差异:

  1. 定义类时使用的类名必须是该项目中没有的类,而定义类别时使用的类名必须是已有的类。
  2. 定义类别时必须使用圆括号来包含类别名。
  3. 类别中通常只定义方法。
    类别实现部分的语法格式如下:
@implmentation 已有类 (类别名)
//方法实现
...
@end

示例:
为NSNumber增加一个类别,类别接口部分的代码如下:

//NSNumber+fk.h
#import <Foundation/Foundation.h>

@interface NSNumber(fk)
-(NSNumber*) add: (double) num2;
-(NSNumber*) substract: (double) num2;
-(NSNumber*) multiply: (double) num2;
-(NSNumber*) divide: (double) num2;
@end	
//NSNumber+fk.m
#import "NSNumber+fk.h"
//为类别提供实现部分
@implementation NSNumber (fk)
-(NSNumber*) add:(double) num2
{
    
    
    return [NSNumber numberWithDouble:([self doubleValue] + num2)];
}
-(NSNumber*) substract:(double)num2
{
    
    
    return [NSNumber numberWithDouble:([self doubleValue] - num2)];
}
-(NSNumber*) multiply:(double)num2
{
    
    
    return [NSNumber numberWithDouble:([self doubleValue] * num2)];
}
-(NSNumber*) divide:(double)num2
{
    
    
    return [NSNumber numberWithDouble:([self doubleValue] / num2)];
}

@end
//NSNumberTest.h
#import <Foundation/Foundation.h>
#import "NSNumber+fk.h"
int main(int argc, const char * argv[]) {
    
    
    @autoreleasepool {
    
    
        NSNumber* myNum = [NSNumber numberWithInt: 3];
        //测试add方法
        NSNumber* add = [myNum add: 2.4];
        NSLog(@"%@ ", add);
        //测试substract方法
        NSNumber* substract = [myNum substract:2.4];
        NSLog(@"%@ ", substract);
        //测试multiply方法
        NSNumber* multiply = [myNum multiply:2.4];
        NSLog(@"%@ ", multiply);
        //测试divide方法
        NSNumber* divide = [myNum divide:2.4];
        NSLog(@"%@", divide);
    }
    return 0;
}
  • 一般习惯将类别的接口文件命名为“类名 + 类别名.h”形式,类别实现部分的文件通常命名为“类名 + 类别名.m”的形式。
  • 通过类别为指定类添加新方法之后,这个新方法不仅会影响NSNumber类, 还会影响NSNumber类的所有子类, 每个子类都会获取类别扩展的方法。
  • 可根据需要为一个类定义多个类别, 不同的类别都可对原有的类增加方法定义。

二、 类别的用法

  • 利用类别对类进行模块化设计
  • 使用类别来调用私有方法

利用类别对类进行模块化设计

当某个类非常大时,如果将该类的所有实现代码放在一个文件中,将会导致这个文件非常大,以至于维护起来非常困难。利用类别对较大的类进行模块化设计的方式,对类实现按模块化分布到不同的*.m 文件中,从而提高项目后期的可维护性。

使用类别来调用私有方法

没有在接口部分定义而是在类实现部分定义的方法相当于私有方法,通常不允许被调用。
但OC实际上并没有真正私有的方法,可使用NSObject 的 performSelector:方法来执行动态调用,但这完全避开了编译器的语法检查,有时候未必是种好的做法。除此之外,还可以通过类别来定义前向引用,从而实现对私有方法的调用。
示例:

FKItem.h:
在这里插入图片描述
FKItem.m
在这里插入图片描述

效果:
在这里插入图片描述

会报错,FKItem对象不能调用calDiscount方法。
为了能在该程序中正常调用calDiscount:方法,程序可以在该main()函数前增加如下类别定义:

//为FKItem定义一个类别
@interface FKItem (fk)
//在类别中前向声明calDiscount:方法
- (double) calDiscount:(double) discount;
@end

增加后的代码:
在这里插入图片描述
效果:
在这里插入图片描述

二、 扩展

扩展与类别相似,扩展相当于匿名类别,定义扩展的语法格式如下:

@interface 已有类 ()
{
    
    
    实例变量
}
//方法定义
...
@end

从以上语法中可以看出,扩展相当于定义一个匿名的类别。但就用法来看,类别通常单独的*. h和 *.m文件,扩展则用于临时对某个类的接口进行扩展,类实现部分同时实现类接口部分定义的方法和扩展中定义的方法。
定义类的扩展时,也可以额外增加实例变量,也可以使用@property、@synthesize来合成setter、getter方法,但定义类的类别时,则不允许额外定义实例变量。

示例:

//FKCar.h
#import <Foundation/Foundation.h>

@interface FKCar : NSObject
@property (nonatomic , copy) NSString* brand;
@property (nonatomic , copy) NSString* model;
- (void)drive;
@end

//FKCar.m
#import "FKCar.h"
#import "FKCar+drive.h"
@implementation FKCar
@synthesize brand;
@synthesize model;
-(void) drive;
{
    
    
    NSLog(@"%@汽车正在路上奔驰", self);
}
-(void) drive:(NSString*) owner
{
    
    
    NSLog(@"%@正在驾驶着%@汽车在路上奔驰", owner, self);
}
-(NSString*)description
{
    
    
    return [NSString stringWithFormat:@"<FK[brand = %@, model = %@, color = %@]>", self.brand, self.model, self.color];
}
@end
//FKCar+drive.h
#import <Foundation/Foundation.h>
#import "FKCar.h"

@interface FKCar ()
@property (nonatomic , copy) NSString* color;
-(void) drive: (NSString*)owner;

@end
//FKCarTest.m
#import <Foundation/Foundation.h>
#import "FKCar+drive.h"
int main(int argc, const char * argv[]) {
    
    
    @autoreleasepool {
    
    
        FKCar* car = [[FKCar alloc]init];
        car.brand = @"宝马";
        car.model = @"X5";
        car.color = @"黑色";
        [car drive];
        [car drive:@"孙悟空"];
    }
    return 0;
}

效果:
在这里插入图片描述

Guess you like

Origin blog.csdn.net/weixin_50990189/article/details/117569132