Objective-C学习 类别和扩展

文章目录

类别的定义与实现

Objective-C的动态允许使用类别为现有的类添加系新方法, 并且不需要创建子类,不需要访问原有类的源代码,

通过使用类别即可动态地为现有类添加新方法, 而且可以将类定义模块化的分布到多个相关的文件中.

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

objective-c 复制代码
@interface 已有类 (类别名) 
    // 方法定义 
    ...
@end

这个语法格式虽然看上去很像在定义类,但是在类名后有一个圆括号, 而且圆括号中带一个类别名.

定义类别名的语法与定义类的语法存在如下差异

  1. 定义类时使用的类名必须是该项目中没有的类, 而定义类别时使用的类名必须是已有的类.
  2. 定义类别时必须使用圆括号来包含类别名.
  3. 类别中通常只定义方法

类别实现部分的语法如下;

objective-c 复制代码
@implementation 已有类 (类别名) 
   // 方法实现
    ...
@end 
objective-c 复制代码
@interface NSNumber (fk)
// 在类别中定义4个方法
- (NSNumber*) add: (double) num2;
- (NSNumber*) substract: (double) num2;
- (NSNumber*) multiply: (double) num2;
- (NSNumber*) divde: (double) num2;
 @end

@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*) divde:(double)num2 {
    return [NSNumber numberWithDouble: [self doubleValue] / num2];
}
@end

int main(int argc, char* argv[]) {
    @autoreleasepool {
        NSNumber* myNum = [NSNumber numberWithInt: 3];
        NSNumber* add = [myNum add: 2.4];
        NSLog(@"%@", add);
        NSNumber* substract = [myNum substract: 2.4];
        NSLog(@"%@", substract);
    }
    return 0;
}

输出:

objective-c 复制代码
5.4
0.6000000000000001

上面为 NSNumber 定义了fk 类别, 并在测试程序中使用 NSNumber 类,该类的实例就会具有add:、substract:、multiply:和 divide:方法,这就实现了对原有 NSNumber 类的动态扩展。

虽然类别可以重写原有类中的方法,但通常并不推荐这么做,如果需要重写原有类的方法,更好的建议是通过原有类派生子类,然后在子类中重写父类原有的方法。关于类别,还有如下两点说明。

  1. 通过类别为指定类添加新方法之后,这个新方法不仅会影响NSNumber 类,还会影响NSNumber 类的所有子类,每个子类都会获取类别扩展的方法。

  2. 可根据需要为一个类定义多个类别,不同的类别都可对原有的类增加方法定义。

类别是 Obiective-℃中一个非常重要的知识,它通常有如下3种用法。

  1. 利用类别对类进行模块化设计。
  2. 使用类别来调用私有方法。
  3. 使用类别来实现非正式协议。

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

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

使用类别来调用私有化方法

除了使用performSelection: 方法来动态调用那些私有方法之外, 还可以通过类别来定义前向引用, 从而实现对私有方法的调用

objective-c 复制代码
@implementation FKItem
@synthesize price;
// 实现接口部分定义的方法
- (void)info
{
    NSLog(@"这是一个普通的方法");
}

// 类实现部分新增的方法,相当于私有方法
- (double)calDiscount:(double)discount
{
    return self.price * discount;
}
@end


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

这样会编译器会提示: FKItem对象不能调用calDiscount: 方法(Objective-C编译器习惯把方法称为selector)

为了能在程序中正常调用calDiscount: 方法, 程序可以在该main() 函数前加如下类别定义

objective-c 复制代码
@interface FKItem (fk) 
    // 在类别中声明 calDiscount: 方法 
- (double) calDiscount: (double) discount: 
@end

这样在 main()函数前定义了该类别之后,FKItem的类别中自然就增加了calDiscount.方法,但类别的方法并不强制程序去实现它--实际上,FKItem类的实现部分已经实现了calDiscount:方法。在该类别中增加定义 calDiscount:方法仅仅是告诉编译器,在 FKItem 类中可以包含 calDiscount:方法,这样编译器就不再提示上面所示的错误了。

扩展

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

objective-c 复制代码
@interface 已有类 () {
    实例变量
}
// 方法定义
.. 
@end

从上面的语法格式中可以看出,扩展相当于定义一个匿名的类别。但就用法来看,类别通常有单独的 *.h 和 *.m 文件,扩展则用于临时对某个类的接口进行扩展,类实现部分同时实现类接口部分定义的方法和扩展中定义的方法。

在定义类的扩展时,可以额外增加实例变量,也可以使用 @property 来合成属性 (包括setter、getter 方法和对应的成员变量), 但定义类的类别时,则不允许额外定义实例变量,也不能用 @property 合成属性。

objective-c 复制代码
#import <Foundation/Foundation.h>

@interface FKCar : NSObject
// 品牌属性
@property (nonatomic, copy) NSString* brand;
// 型号属性
@property (nonatomic, copy) NSString* model;
// 无参drive方法
- (void)drive;
@end 

// 定义FKCar的类扩展
@interface FKCar ()
// 颜色属性
@property (nonatomic, copy) NSString* color;
// 带参drive方法
- (void)drive:(NSString*)owner;
@end 

    
@implementation FKCar
// 实现无参 drive 方法
- (void)drive
{
    NSLog(@"%@汽车正在路上奔驰", self);
}
// 实现带参 drive 方法
- (void)drive:(NSString*)owner
{
    NSLog(@"%@正驾驶%@汽车在路上奔驰", owner, self);
}
- (NSString*)description
{
    return [NSString stringWithFormat:@"<FK[_brand=%@, _model=%@, _color=%@]>", 
            self.brand, self.model, self.color];
}
@end

在上面的程序中,可以使用点语法来访问 color 属性,这个 color 属性是在扩展部分定义的。除此之外,也可调用 FKCar 对象的 drive: 方法,这个方法去也是在扩展部分定义的

相关推荐
Sakinol#2 小时前
Leetcode Hot 100 ——回溯part02
算法·leetcode
ArturiaZ2 小时前
【day53】
开发语言·c++·算法
历程里程碑2 小时前
36 Linux线程池实战:日志与策略模式解析
开发语言·数据结构·数据库·c++·算法·leetcode·哈希算法
red_redemption2 小时前
自由学习记录(133)
学习
可编程芯片开发2 小时前
基于自适应MUSIC算法的波束形成matlab仿真
算法·matlab·波束形成·自适应music
2301_789015622 小时前
DS进阶:红黑树
c语言·开发语言·数据结构·c++·算法·r-tree·lsm-tree
¿i?2 小时前
吃什么?作业复习LinkedList==DEBUG
数据结构·c++·学习
郝学胜-神的一滴2 小时前
深度学习浪潮:解锁技术边界与产业新图景
数据结构·人工智能·python·深度学习·算法
daxi1502 小时前
C语言从入门到进阶——第13讲:深入理解指针(3)
c语言·开发语言·数据结构·算法