【iOS】—— 协议(protocol)和委托

协议的作用:

类似于接口,用于定义多个类应该遵守的规范,但不会为这些方法提供实现,方法的实现则交给类去完成。

规范、类和实例的抽象关系

sd

由图可看出,同一个类的内部状态数据、各种方法的实现细节完全相同,类是一种具体实现体。而协议则定义了了一种规范,协议定义某一批类所需要遵守的规范,它不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它只规定这批类中必须提供某些方法,提供这些方法的类就可满足实际需要。

协议不提供任何实现。协议体现的是规范和实现分离的设计哲学。让规范和实现分离也正是协议的好处,是一种松耦合的设计。

使用类别实现非正式协议

类别(catgory)可以实现非正式协议,这种类别以NSObject为基础,为NSObject创建类别,创建类别时即可指定该类别应该新增的方法。

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

例如:

以NSObject为基础,定义一个类别,类别名称为Eatable,代码如下
#import <Foundation/Foundation.h>

//以NSObject为基础定义Eatable类别
@interface NSObject (Eatable)
- (void) taste;
@end

上面为NSObject的Eatable类别中定义了一个taste:方法,接下来所有继承NSObject类的子类都会自动带有该方法,而且NSObject的子类可以根据需要,自行决定是否要实现该方法。

Eatable类别作为一个非正式协议使用,那么相当于定义了一个规范,因此,遵守该协议的子类通常都会实现这个方法。

再为NSObject(Eatable)派生一个子类:
#import <Foundation/Foundation.h>
#import "NSObject+Eatable.h"

//定义类的接口部分
@interface FKApple : NSObject
@end
#import "FKApple.h"

//为FKApple提供实现部分
@implementation FKApple
- (void) taste {
    NSLog(@"苹果营养丰富,口味很好!");
}
@end

从上述的程序可以看到,虽然接口部分没有定义任何方法,但是这丝毫不会影响到该类的功能,因为它继承了NSObject(Eatable),只要在FKApple类的实现部分实现taste方法即可。

下面是FKApple类的测试代码:
#import <Foundation/Foundation.h>
#import "FKApple.h"

int main(int argc, char* argv[]) {
    @autoreleasepool {
        FKApple* app = [[FKApple alloc] init];
        [app taste];
    }
    return 0;
}

输出结果为:
21

Objective-C并不强制实现该协议中的所有方法,但是若不实现FKApple类中的taste方法,而且非正式协议本身也没有实现该方法,就会引发下面的错误:
34

正式协议的定义

正式协议的基本语法如下:
@protocol 协议名 <父协议1, 父协议2>
{
   零个到多个方法定义...
}

上面语法的详细说明如下:

  • 协议名与类名采用相同的规则命名。
  • 一个协议可以有多个直接父协议,但协议只能继承协议,不能继承类。
  • 协议中定义的方法只有方法签名,没有方法实现;协议中包含的方法既可是类方法,也可是实例方法。

协议定义的是多个类共同的公共行为规范,因此,协议里所有的方法都是公开访问权限。

下面定义一个协议:
#import <Foundation/Foundation.h>

//定义协议
@protocol Output
//定义协议的方法
- (void) output;
- (void) addData: (NSString*) msg;
@end

上面定义了一个Output协议,其中定义了两个方法,而协议并不关心方法的实现,就相当于定义了一个接口。

再定义一个Productable协议,该协议代表了所有的产品都需要遵守的规范:
#import <Foundation/Foundation.h>

//定义协议
@protocol Productable
//定义协议的方法
- (NSDate*) getProduceTime;
@end
接下来定义一个打印机协议,该协议同时继承上面两个协议:
#import <Foundation/Foundation.h>
#import "Output.h"
#import "Productable.h"

//定义协议,继承了上面两个协议
@protocol Printable <Output, Productable>
//定义协议的方法
- (NSString*) PrintColor;
@end

协议的继承和类继承不一样,协议完全支持多继承,即一个协议可以有多个直接的父协议。和类继承相似,子协议继承某个父协议,将会获得父协议里定义的所有方法。

一个协议继承多个父协议时,多个父协议排在<>中间,多个协议口之间以英文逗号( , )隔开。

遵守(实现)协议

在类定义的接口部分可指定该类继承的父类,以及遵守的协议,语法如下:
@interface 类名: 父类 <协议1, 协议2...>

从上面的语法格式可以看出,一个类可以同时遵守多个协议。

为Printable协议提供一个实现类:Printer,该实现类的接口部分代码如下:
#import <Foundation/Foundation.h>
#import "Printable.h"

//定义类的接口部分,继承NSObject,遵守Printable协议
@interface Printer :  NSObject <Printable>
@end
Printer类实现部分代码如下:
#import "Printer.h"
#import "Printable.h"
#define MAX_CACHE_LINE 10

@implementation Printer
{
    NSString* printData[MAX_CACHE_LINE];
    int dataNum;
}
- (NSString*) PrintColor {
    return @"红色";
}
- (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];
}
@end

Printer类实现了Printable协议,且也实现了Printable和Printable的两个父协议中的所有方法。假如实现类没有实现协议中的PrintColor方法,编译器会提示如下警告。

342

测试程序的代码如下:
#import <Foundation/Foundation.h>
#import "Printer.h"

int main(int argc, char* argv[]) {
    @autoreleasepool {
        //创建Printer对象
        Printer* printer = [[Printer alloc] init];
        //调用Printer对象的方法
        [printer addData:@"疯狂iOS讲义"];
        [printer addData:@"疯狂XML讲义"];
        [printer output];
        [printer addData:@"疯狂Android讲义"];
        [printer addData:@"疯狂Ajax讲义"];
        [printer output];
        //创建一个Printer对象,当成Productable使用
        NSObject<Productable>* p = [[Printer alloc] init];
        //调用Productable协议中定义的方法
        NSLog(@"%@", p.getProduceTime);
        //创建一个Printer对象,当成Output使用
        id<Output> out = [[Printer alloc] init];
        //调用Output协议中定义的方法
        [out addData:@"孙悟空"];
        [out addData:@"猪八戒"];
        [out output];
    }
    return 0;
}

输出结果如下:
323

程序中的两行粗体字代码没有使用Printer来定义变量,而是使用协议来定义变量,那么这些变量只能调用该协议中声明的方法,否则编译器会提示错误。

如果程序需要使用协议来定义变量,有如下两种方法:
  • NSObject<协议1, 协议2…>* 变量;
  • id<协议1, 协议2…> 变量;
    通过上面的语法格式定义的变量,它们的编译时类型仅仅只是所遵守的协议类型,因此只能调用该协议中定义的方法。
正式协议与非正式协议的差异:
  • 非正式协议通过为NSObject创建类别来实现,而正式协议则直接使用@protocol创建。
  • 遵守非正式协议通过继承带特定类别的NSObject来实现;而遵守正式协议则有专门的Objective-C语法。
  • 遵守非正式协议不要求实现协议中定义的所有方法;而遵守正式协议则必须实现协议中定义的所有方法。
为了弥补遵守正式协议必须实现协议的所有方法造成灵活性不足,OC新增了@optional、@required两个关键字,其作用如下:
  • @optional:位于该关键字之后、@required或@end之前声明的方法时可选的——实现类既可以选择实现这些方法,也可以不实现这些方法。
  • @required:位于该关键字之后、@optional或@end之前声明的方法是必需的——实现类必须实现这些方法。如果没有实现这些方法,编译器就会提示警告。@required是默认行为。

例如定义如下协议:

@protocol Output
//定义协议的方法
@optional
- (void) output;
@required
- (void) addData: (NSString*) msg;
@end

上面协议中定义了两个方法,该协议的实现类可选实现output方法,但必须实现addData:方法,否则编译器就会提示警告。

协议与委托(delegate)

委托的定义:

就是某个对象指定另一个对象处理某些特定任务的设计模式。通俗来说,就是“某个对象”把要做的事情委托给“另一个对象”去做。

其中“某个对象”被称作委托者,“另一个对象”被称作是被委托者,即代理。

委托和代理的关系

如下图,委托方通过某种方式把任务分派出去给代理方处理,而两者之间的联系便是协议。

324

在程序中:一般情况下

委托需要做的工作有:
  • 定义协议与方法
  • 声明委托变量
  • 设置代理
  • 通过委托变量调用委托方法
代理需要做的工作有:
  • 遵循协议
  • 实现委托方法
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值