【OC】类别与扩展

类别

  通过使用类别,我们可以动态地为现有的类添加新方法,还可以将类定义模块化地分布到多个相关文件中。
  类别同样由接口部分和实现部分组成,语法格式如下:

接口部分语法格式:

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

  虽然该语法格式看起来很像在定义类,但定义类别的语法与定义类的语法存在很多差异,如下:

在这里插入图片描述
实现部分语法格式:

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

下面是对NSNumber增加一个类别的代码实现。

接口部分:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//定义一个类别
@interface NSNumber (fk)
//在类别中定义4个方法
- (NSNumber*) jia: (double) num2;
- (NSNumber*) jian: (double) num2;
- (NSNumber*) cheng: (double) num2;
- (NSNumber*) chu: (double) num2;
@end

NS_ASSUME_NONNULL_END

实现部分:

#import "NSNumber+fk.h"

//为类别提供实现部分
@implementation NSNumber (fk)
//实现类别的接口部分定义的4个方法
- (NSNumber*) jia: (double) num2
{
    return [NSNumber numberWithDouble: ([self doubleValue] + num2)];
}
- (NSNumber*) jian: (double) num2
{
    return [NSNumber numberWithDouble: ([self doubleValue] - num2)];
}
- (NSNumber*) cheng: (double) num2
{
    return [NSNumber numberWithDouble: ([self doubleValue] * num2)];
}
- (NSNumber*) chu: (double) num2
{
    return [NSNumber numberWithDouble: ([self doubleValue] / num2)];
}
@end

主函数:

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber* a = [NSNumber numberWithInt: 3];
        //测试jia:方法
        NSNumber* jia = [a jia: 3.14];
        NSLog(@"%@", jia);
        //测试jian:方法
        NSNumber* jian = [a jian: 1.1];
        NSLog(@"%@", jian);
        //测试cheng:方法
        NSNumber* cheng = [a cheng: 1.1];
        NSLog(@"%@", cheng);
        //测试chu:方法
        NSNumber* chu = [a chu: 2.0];
        NSLog(@"%@", chu);
    }
    return 0;
}

运行结果:
在这里插入图片描述
不同写法:

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSNumber* a = [NSNumber numberWithInt: 3];
        //测试jia:方法
        NSNumber* jia = [a jia: 3.14];
        NSLog(@"%lf", [jia doubleValue]);
        //测试jian:方法
        NSNumber* jian = [a jian: 1.11];
        NSLog(@"%lf", [jian doubleValue]);
        //测试cheng:方法
        NSNumber* cheng = [a cheng: 1.11];
        NSLog(@"%lf", [cheng doubleValue]);
        //测试chu:方法
        NSNumber* chu = [a chu: 2.0];
        NSLog(@"%lf", [chu doubleValue]);
    }
    return 0;
}

运行结果:

  虽然类别可以重写原有类中的方法,但通常不建议这么做,如果需要重写原有类的方法,更好的建议是通过原有类派生子类,然后在子类中重写父类原有的方法

关于类别的两点说明和三种用法:

在这里插入图片描述

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

  在前面类的设计中,可以使用*.h 文件来定义类接口部分,使用*.m 文件来定义类实现部分,但不能将类实现部分分布到多个*.m 文件中。当某个类非常大时,如果将该类的所有实现代码放在一个文件中,将会导致这个文件非常大,以至于维护起来非常困难。

  如果需要,可以使用类别将一个较大的类进行分模块设计,以便于进行代码的维护。

使用类别来调用私有方法

  在前面的学习中我们知道,在实现部分定义的方法相当于私有方法,通常不允许调用。但是实际上,OC并没有真正的私有方法,使用NSObject的performSelector:方法来执行动态调用,就可以调用那些私有方法。
  除了使用performSelector:方法来执行动态调用私有方法外,我们还可以通过类别来定义前向引用,从而实现私有方法的调用。

代码示例如下:

接口部分:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface FKItem : NSObject

@property (nonatomic, assign) double price;
- (void) info;

@end

NS_ASSUME_NONNULL_END

实现部分:

#import "FKItem.h"

@implementation FKItem

@synthesize price;
//实现接口部分定义的方法
- (void) info
{
    NSLog(@"这是最开始的info方法");
}
//额外新增的方法(即私有方法)
- (double) calDiscount: (double) discount
{
    return self.price * discount;
}

@end

  该实现部分的新增方法即为私有方法,若我们直接通过FKItem对象调用calDiscount:方法必然会出错,为了能在程序中正常调用calDiscount:方法,我们可以在main()函数前面加上一个类别定义,完整主函数部分如下:

#import <Foundation/Foundation.h>
#import "FKItem.h"

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        FKItem* item = [[FKItem alloc] init];
        item.price = 100;
        [item info];
        NSLog(@"物品打折后的价格为:%g", [item calDiscount: 0.75]);
    }
    return 0;
}

运行结果:
在这里插入图片描述

扩展

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

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

  从语法上来看,扩展相当于定义一个匿名的类别,但从用法来看,类别一般是有单独的*.h和*.m文件,扩展则用于临时对某个类的接口进行扩展,类实现部分同时实现类接口部分定义的方法和扩展中定义的方法。
  定义类的扩展的时候,可以额外增加实例变量,也可以用@property来合成属性(包括setter和getter方法和对应的成员变量),但定义类的类别的时候,是不允许额外定义实例变量和合成属性的。

代码示例:

接口部分:

#import <Foundation/Foundation.h>
 
NS_ASSUME_NONNULL_BEGIN
 
@interface FKCar : NSObject
 
@property (nonatomic,copy) NSString *brand;
@property (nonatomic,copy) NSString *model;
 
- (void) drive;
 
@end
 
NS_ASSUME_NONNULL_END

扩展部分新建*.h文件时,一般建议在File处写成扩展类别名,在Class部分写成被扩展类,在File Type部分记得选择Extension。
在这里插入图片描述

扩展的接口部分:

#import <Foundation/Foundation.h>
#import "FKCar.h"
 
NS_ASSUME_NONNULL_BEGIN
 
//定义FKCar的扩展
@interface FKCar ()
@property (nonatomic, copy) NSString* color;
- (void) drive: (NSString*) owner;
 
@end
 
NS_ASSUME_NONNULL_END

实现部分:

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

主函数:

#import <Foundation/Foundation.h>
#import "FKCar+drive.h"
 
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建FKCar对象
        FKCar* car = [[FKCar alloc] init];
        //使用点语法为car对象的属性赋值
        car.brand = @"宝马";
        car.model = @"X5";
        car.color = @"黑色";
        //调用car方法
        [car drive];
        [car drive: @"孙悟空"];
    }
    return 0;
}

运行结果:
在这里插入图片描述

  • 39
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值