OC--类别 扩展 协议与委托

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

类别类似于继承又与继承不同
通过继承,子类可以在父类的基础上添加新的方法,审议重写父类已有的方法。
当我们需要为已有的类拓展一些新的行为的时候,最容易想到的就是继承。但有些时候,继承不是最好的选择。这些时候就要用到类别。


一、类别

1.什么时候使用类别?

比如希望为NSNmber类增添一些新的方法时,由于NSNumber只是一个类簇(cluster)的前端类,通过[NSNumber numberWithInt: 5]方法所生产的NSNumber对象只是NSNumber子类的实例。这样即使为NSNumber派生子类也没有任何意义。派生出来的子类对当前类没有任何影响。此时就要借助类别来实现

类簇:OC总是main想父类编程的。当程序调用父类的初始化方法,类方法来返回对象时,实际上返回的是子类的实例。这一系列的类就被称为一个类簇(cluster),这个父类也就模拟了抽象类的功能。

2.使用类别的好处

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

类的接口部分:

#import<Foundation/Foundation.h>
@interface NSNumber(fk)
-(NSNumber*)add: (double)num2;
-(NSNumber*)substract: (double)num2;
-(NSNumber*)multiply: (double)num2;
-(NSNumber*)divide: (double)num2;
@end

类的实现部分:

#import"heihei.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

测试:

#import<Foundation/Foundation.h>
#import"heihei.h"
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);
        NSNumber* multiply = [myNum multiply:2.4];
        NSLog(@"%@", multiply);
        NSNumber* divide = [myNum divide:2.4];
        NSLog(@"%@", divide);
    }
}

以下是代码运行结果,运行结果有偏差是因为浮点数在底层存储时有精度丢失
在这里插入图片描述
虽然类别可以重写原来类中的方法 ,但不推荐这样做。如果需要重写原有类中的方法,更好的建议是通过原有类派生子类,然后在子类中重写父类原有的方法。

通过类别为制定类添加子类后,会影响制定类的所有子类。每个子类都会获取类别拓展的方法。

可根据需要为一个类定义多个类别,不同的类别都可以对原有类增加方法定义。使用方法如下:

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

3.使用类别来进行模块化设计

  • 什么是模块化设计?
    通过类别将一个较大的类分模块设计,可以实现对类按模块分布到不同的*.m文件中,提高程序的可维护性。

4.使用类别来调用私有方法

  • 什么是私有方法?
    没有在类的接口部分定义而是在类的实现部分定义的方法相当于私有化方法。但是OC并没有真正的私有化方法,如果使用NSObject的performSelector:方法来执行动态调用,则完全可以调用私有方法。但是这样做会完全避开编译器的语法检查,所以还可以通过类别定义前向引用。

这里是代码接口部分

#import<Foundation/Foundation.h>
@interface Item: NSObject
@property(nonatomic, assign)double price;
-(void)info;
@end

类的实现部分

#import"Apple.h"
@implementation Item
@synthesize price;
-(void)info {
    NSLog(@"这是一个普通的方法");
}
//这是新增方法
-(double) calDiscount:(double) discount {
    return self.price* discount;
}
@end

测试:

#import<Foundation/Foundation.h>
#import"Apple.h"
int main(int argc, char* argv[])
{
    @autoreleasepool{
        Item* item=[[Item alloc]init];
        item.price=109;
        [item info];
        NSLog(@"物品打折的价格为:%g", [item calDiscount:.75]);
        
    }
}

会出现如下报错。这说明直接调用私有方法行不通。此时就要用到类别
在这里插入图片描述
使用类别代码如下

#import<Foundation/Foundation.h>
#import"Apple.h"
@interface Item(fk)
-(double)calDiscount: (double)discount;
@end
int main(int argc, char* argv[])
{
    @autoreleasepool {
        Item* item=[[Item alloc]init];
        item.price=109;
        [item info];
        NSLog(@"物品打折的价格为:%g",[item calDiscount:.75]);
        
    }
}

此时代码就能顺利运行。这就是使用类别来调用私有方法
在这里插入图片描述

二、扩展

1.什么是扩展?

扩展与类别相似,扩展相当于匿名类别

但是类别通常有单独的*.h或*.m文件。而拓展则用于临时对某个类的接口进行扩展。在类的实现部分实现类接口部分定义的方法和扩展中定义的方法
在定义类的扩展时,可以额外的增加实例变量,也可以使用@property来合成属性,但在定义类的类别时,不允许额外定义实例变量,也不能使用@property合成属性

2.拓展的代码实现

类的接口部分

#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()
//这里定义了color属性和drive方法
@property (nonatomic,copy)NSString* color;
-(void)drive:(NSString*)owner;
@end

类的实现部分

#import "Apple.h"

@implementation FKCar
-(void)drive{
    NSLog(@"%@汽车正在路上奔驰",self);
}
-(void)drive:(NSString*)owner{
    NSLog(@"%@正驾驶%@汽车在路上奔驰",owner,self);
}
-(NSString*)descripition{
    return [NSString stringWithFormat:@"[_brand=%@,_model=%@,_color=%@]",self.brand,self.model,self.color];
}
@end

测试

int main(){
    @autoreleasepool {
        Car* car=[[Car alloc]init];
        car.brand=@"nuonuo";
        car.model=@"mx";
        car.color=@"red";
        [car drive];
        [car drive:@"nuonuo"];
    }
}

代码结果如下
在这里插入图片描述

三、协议

协议的作用类似于接口,用于定义多个类应该遵守的规范
OC中协议的作用就相当于其他语言中接口的作用

1.协议是什么?

1.协议规定了一种规范,定义某一批类所需要遵守的规范。
2.类是一种具体的实现题,同一个类的内部状态数据,各种方法的实现细节完全相同。
3.协议不提供任何实现。协议体现的是规范和实现分离的设计哲学

协议定义的是多个类的共同点行为规范。这些行为是与外部交流的通道。
协议通常是定义一组共用方法,但不会为这些方法提高实现,方法的实现交给类去完成

2.非正式协议

  • 当某个类实现NSObject的该类别时,就需要实现该类别下的所有方法。这种基于NSObject定义的类别即可认为是非正式协议。

定义一个名为Esaable的类别

#import<Foundation/Foundation.h>
@interface NSObject (Eatble)
-(void)taste;
@end

为Estable派生一个子类

#import"heihei.h"
@interface FKApple:NSObject
@end

类的实现

#import"Apple.h"
@implementation FKApple
-(void) taste{
    NSLog(@"很怪");
}
@end

测试

#import<Foundation/Foundation.h>
#import"Apple.h"
int main(){
    @autoreleasepool {
        FKApple* a=[[FKApple alloc]init];
        [a taste];
    }
}

结果
在这里插入图片描述

OC并不强制遵守飞正式协议的类必须实现该协议中所有方法,但如果没有实现非正式协议中的某个方法,那么程序运行时调用该方法就会引发unrecognized selector的错误

3.正式协议

正式协议不再使用@interface,@implementation等关键字。而是使用@protocol
基本语法格式如下

在这里插入图片描述
1.协议名与类名采用相同的命名规则
2.一个协议可以有多个直接父协议,但协议只能继承协议,不能继承类
3.协议中定义的方法只有方法签名,没有实现
4.协议中包含的方法既是类方法,也可以是实例方法
5.协议中的所有方法都具有公开的访问权限
定义一个协议:

@protocol  FKOutput
-(void)output;
-(void)addData:(NSString*) msg;

@end

在定义一个协议:

#import<Foundation/Foundation.h>
@protocol FKProductable
-(NSDate*)getProduceTime;
@end

定义一个协议继承上边两个协议

#import<Foundation/Foundation.h>
#import"FKOutput.h"
#import"FKProductable.h"
@protocol FKPrintable<FKOutput,FKProductable>
-(NSString*)printColor;
@end

类的实现:

#import"FKPrintable.h"
#import"Apple.h"
#define MAX_CACHE_LINE 10
@implementation FKPrinter{
    NSString* printData[MAX_CACHE_LINE];
    int dataNum;
}
-(void)output{
    while(dataNum>0){
        NSLog(@"打印机使用%@打印:%@",self.printColor,printData[0]);
        dataNum--;
        for(int i=0;i<dataNum;i++){
            printData[i]=printData[i+1];
        }
    }
}
-(void)addData:(NSString*)msg{
    if(dataNum>=MAX_CACHE_LINE){
        NSLog(@"输出队列已满,添加失败");
        
    }else{
        printData[dataNum++]=msg;
    }
}
-(NSDate*)getProduceTime;{
    return [[NSDate alloc]init];
}
-(NSString*)printColor{
    return @"红色";
}
@end

测试:

#import<Foundation/Foundation.h>
#import"FKPrintable.h"
#import"Apple.h"
int main(int argc,char* argv[]) {
    @autoreleasepool {
            FKPrinter* printer = [[FKPrinter alloc]init];
            [printer addData: @"娜娜"];
            [printer addData: @"娜娜"];
            [printer output];
            [printer addData: @"神奇"];
            [printer addData: @"海螺"];
            [printer output];
            NSObject<FKProductable>* p = [[FKPrinter alloc]init];
            NSLog(@"%@", p.getProduceTime);
            id<FKOutput>out=[[FKPrinter alloc]init];
            [out addData: @"挪挪"];
            [out addData: @"神奇"];
            return 0;
    }
}

代码结果:
在这里插入图片描述

类的接口部分遵守协议的格式:
在这里插入图片描述
如果程序需要用协议来定义变量

NSObject<协议1,协议2…>*变量;
id<协议1,协议2…>变量;

非正式协议正式协议
NSObject创建类别@protocol创建
通过继承带特定类别的NSObject有专门的OC语法
不要求实现协议中定义的所有方法必须实现协议中的所有方法

为了弥补遵守正式协议必须实现协议所有方法的不足,OC新增了@optional,@required两个关键字

@optional:位于该关键字之后,@optional或@end之前声明的方法是可选择是否实现的
@required位于该关键字之后,@required或@end之前的声明方法是必须实现的

如:
在这里插入图片描述
output方法可以选择要不要实现,而addData:方法必须实现

通过这两个关键字的使用,正式协议可以完全取代非正式协议

四、协议与委托

协议体现的是一种规范,定义协议的类可以把协议定义的方法委托给实现协议的类,可以让协议具有更好的通用性。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山河丘壑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值