疯狂iOS讲义学习笔记-类别与扩展

1.类别(category)

1.1 类别的基本知识

Objective-C的动态特征允许类别为现有的类添加方法,并且不需要创建子类,不需要访问原有类的源代码。
类别同样由接口部分和实现部分组成,接口部分的语法格式如下:

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

这个语法格式看上去很像在定义类,但在类名后面有一个圆括号,而且圆括号中带一个类别名。
类实现部分的语法格式如下:

@implementation 已有类 (类别名)
// 方法实现
@end

尝试为NSNumber增加一个类别,接口部分如下:

@interface NSNumber (fk)
- (NSNumber*) add:(double) num2;
- (NSNumber*) substract:(double) num2;
- (NSNumber*) multipy:(double) num2;
- (NSNumber*) divide:(double) num2;
@end

就习惯而言,一般将类别接口文件命名为“类名+类别名.h”的形式,将实现部分的文件命名为“类名+类别名.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*) multipy:(double) num2 {
    return [NSNumber numberWithDouble:([self doubleValue] * num2)];
}
- (NSNumber*) divide:(double) num2 {
    return [NSNumber numberWithDouble:([self doubleValue] / num2)];
}
@end

使用如下程序测试NSNumber的fk类别:

#import "NSNumber+fk.h"

int main(void) {
    @autoreleasepool {
        NSNumber* num1 = [NSNumber numberWithDouble:3];
        NSNumber* add = [num1 add:2.4];
        NSLog(@"%@", add);
        NSNumber* substract = [num1 substract:2.4];
        NSLog(@"%@", substract);
        NSNumber* multipy = [num1 multipy:2.4];
        NSLog(@"%@", multipy);
        NSNumber* divide = [num1 divide:2.4];
        NSLog(@"%@", divide);
    }
}

运行该程序,可以看到如下输出:

2022-05-26 15:04:42.084176+0800 oc.programme[90184:3166256] 5.4
2022-05-26 15:04:42.084687+0800 oc.programme[90184:3166256] 0.6000000000000001
2022-05-26 15:04:42.084712+0800 oc.programme[90184:3166256] 7.199999999999999
2022-05-26 15:04:42.084726+0800 oc.programme[90184:3166256] 1.25

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

当一个类非常大时,如果我们将其实现部分放到一个文件中,该文件将非常大,不利于我们后期维护。这时,我们可以通过类别将其实现部分分到不同文件中。
例如NSWindow,在它的头文件“NSValue.h”中可以看到NSValue如下定义

@interface NSValue : NSObject <NSCopying, NSSecureCoding>

还有如下类别:

@interface NSValue (NSValueCreation)
@interface NSValue (NSValueExtensionMethods)
@interface NSValue (NSDeprecated)

通过这种方式,NSValue可以提供NSValue.m、NSValue+NSValueCreation.m、NSValue+NSValueExtensionMethods.m和NSValue+NSDeprecated.m实现文件,这样就可以对类实现按模块不同分布到不同的*.m文件中,从而提高后期的可维护性。


1.3 使用类别调用私有方法

我们知道,在类实现部分实现而未在类接口部分声明的方法相当于私有方法,通常不允许被调用。但是,Objective-C实际上并没有真正的私有方法,如果使用NSObject的performSelestor:方法执行动态调用,完全可以调用那些私有方法。
除此以外,我们还可以通过类别来定义前向引用,从而实现对私有方法的调用。
如下程序,我们定义了FKCar类,并在它的实现部分添加一个私有方法。

#import <Foundation/Foundation.h>

@interface FKCar : NSObject
@property (nonatomic, assign) double price;
- (void) info;
@end

@implementation FKCar
- (void) info {
    NSLog(@"我是一个普通的方法");
}
// 定义私有方法
- (double) claDiscount:(double) discount {
    return self.price * discount;
}
@end

然后,我们在main函数前使用类别声明FKCar的私有方法,并尝试在main函数中调用:

#import "FKCar.h"

@interface FKCar (fk)
- (double) calDiscount:(double) discount;
@end

int main(void) {
    @autoreleasepool {
        FKCar* car = [[FKCar alloc] init];
        car.price = 1000;
        double discount = 0.87;
        // 调用普通方法
        [car info];
        // 调用私有方法
        NSLog(@"%g", [car calDiscount:discount]);
    }
}

运行程序,输出结果:

2022-05-26 16:12:35.063257+0800 oc.programme[90947:3194563] 我是一个普通的方法
2022-05-26 16:12:35.063446+0800 oc.programme[90947:3194563] 870

2. 扩展

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

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

扩展通常用于临时对某个类的借口进行扩展,类实现部分同时实现类接口部分和扩展部分中定义的方法。

定义类的扩展时,允许额外定义实例变量,而定义类的类别时,不允许定义额外的实例变量。
例如下的程序:

#import <Foundation/Foundation.h>

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

上面定义了类的接口,下面对其进行扩展:

#import "FKCar.h"

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

现在,我们为其添加实现部分:

#import "FKCar+drive.h"
@implementation FKCar
- (void) drive {
    NSLog(@"%@正在路上行驶", self.brand);
}
- (void) drive:(NSString*) name {
    NSLog(@"%@正在驾驶着%@的%@在路上行驶", name, self.color, self.brand);
}
@end

测试程序如下:

#import "FKCar+drive.h"

int main(void) {
    @autoreleasepool {
        FKCar* car = [[FKCar alloc] init];
        // 使用点语法为car对象的属性赋值
        car.brand = @"宝马";
        car.model = @"01";
        car.color = @"黑色";
        // 调用接口部分的方法
        [car drive];
        // 调用扩展部分的方法
        [car drive:@"老王"];
    }
}

输出结果如下:

2022-05-26 16:44:29.035156+0800 oc.programme[91282:3208315] 宝马正在路上行驶
2022-05-26 16:44:29.035371+0800 oc.programme[91282:3208315] 老王正在驾驶着黑色的宝马在路上行驶
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值