OC协议与委托

Posted Billy Miracle

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OC协议与委托相关的知识,希望对你有一定的参考价值。

协议的作用类似于接口,用于定义多个类应该遵守的规范。

规范、协议与接口

同一个类的内部状态数据、各种方法的实现细节完全相同,类是一种具体的实现体。而协议则定义了一种规范,协议定义某一批类所需要遵守的规范,它不关心类的内部状态数据,也不关心类里方法的实现细节,它只规定这批类中必须提供某些方法,提供这些方法的类就可满足实际需要。
协议不提供任何实现。协议体现的是规范和实现分离的设计哲学。
Objective-C中协议的作用就相当于其他语言中接口的作用。
协议定义的是多个类共同的公共行为规范,这意味着协议里通常是定义一组共用方法,但不会为这些方法提供实现,方法的实现则交给类去完成。

使用类别实现非正式协议

之前介绍过,类别可以实现非正式协议,这种类别以NSObject为基础,为NSObject创建类别,创建类别时即可指定该类别应该新增的方法。
当某个类实现NSObject的该类别时,就需要实现该类别下的所有方法,这种基于NSObject定义的类别即可认为是非正式协议。
非正式协议实际上是一个分类,列出了一组方法但并没有实现它们。非正式协议通常是为根类定义的,有时,非正式协议也称为抽象协议。
示例:

//  NSObject+Eatable.h

#import <Foundation/Foundation.h>

@interface NSObject (Eatable)
-(void) taste;
@end

上面在NSObject的Eatable类别中定义了一个taste方法,接下来所有继承NSObjective类的子类都会自动带有该方法,子类可以根据自己的需要决定是否实现该方法。Eatable类别作为一个非正式协议使用,相当于定义了一个规范,因此,遵守该协议的子类通常都会实现这个方法。

//  Apple.h

#import <Foundation/Foundation.h>
#import "NSObject+Eatable.h"

@interface Apple : NSObject

@end

Apple是一个空类,未定义任何方法。

#import "Apple.h"

@implementation Apple
-(void) taste {
    NSLog(@"Apple tastes good!");
}
@end

Apple实现了taste方法,这样,apple类就相当于遵守了Eatable协议,接下来就可以把Apple类当成Eatable对象来调用。

//  main.m

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

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

最后需要指出的是,对于实现非正式协议的类而言,OC并不强制实现该协议中的所有方法。如果该类没有实现非正式协议中的某个方法,那么程序运行时调用该方法,就会引发unrecognized selector错误。

正式协议的定义

和定义类不同,正式协议不再使用@interface、@implementation关键字,而是使用@protocal关键字。定义正式协议的基本语法格式如下:

@protocol 协议名 <父协议1 , 父协议2> {
	零到多个方法定义...
}

说明:

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

协议里所有的方法都具有公开的访问权限。
示例:

//  Output.h

#import <Foundation/Foundation.h>

@protocol Output <NSObject>

-(void) output;
-(void) addData: (NSString*) msg;

@end

上面定义了一个Output协议,定义了添加数据的 addData: 和表示输出的 output 方法。
接下来再定义一个 Productable 协议:

//  Productable.h

#import <Foundation/Foundation.h>

@protocol Productable <NSObject>

-(NSDate*) getProduceTime;

@end

定义了一个 getProduceTime 方法,该方法返回产品的生产时间。
接下来定义一个打印协议,该协议同时继承上面的两个协议:

//  Printable.h

#import <Foundation/Foundation.h>
#import "Output.h"
#import "Productable.h"

@protocol Printable <NSObject , Output , Productable>
//继承两个协议
-(NSString*) printColor;

@end

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

遵守(实现)协议

在类定义的接口部分可指定该类继承的父亲,以及遵守的协议,语法格式如下:

@interface 类名 : 父类 <协议1 , 协议2...>

一个类可以同时遵守多个协议。

//  Printer.h

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

@interface Printer : NSObject <Printable>

@end
//  Printer.m

#import "Printer.h"
#define MAX_CACHE_LINE 10

@implementation Printer {
    NSString* printData[MAX_CACHE_LINE];//使用数组记录所有需要缓存的打印数据
    int dataNum;//记录当前需打印作业数
}
-(void) output {
    //只要还有作业,就继续打印
    while (dataNum > 0) {
        NSLog(@"打印机使用%@打印:%@" , self.printColor ,printData[0]);
        //将剩下的作业数减1
        dataNum--;
        //把作业队列整体前移一位
        for (int i = 0; i < dataNum; i++) {
            printData[i] = printData[i + 1];
        }
    }
}
-(void) addData:(NSString *)msg {
    if (dataNum >= MAX_CACHE_LINE) {
        NSLog(@"输出队列已满,添加失败");
    } else {
        //把打印数据添加到队列里,已保存数据的数量加1
        printData[dataNum++] = msg;
    }
}
-(NSDate*) getProduceTime {
    return [[NSDate alloc] init];
}
-(NSString*) printColor {
    return @"红色";
}

@end

上面的Printer类实现了Printable协议,并且也实现了Output 和 Productable 两个父协议中的所有方法。如果实现类实现了协议中的所有方法,那么程序就可以调用该实现类所实现的方法。
代码:

//  main.m

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        //创建Printer对象
        Printer* myprinter = [[Printer alloc] init];
        //调用Printer对象的方法
        [myprinter addData:@"ABC"];
        [myprinter addData:@"EFG"];
        [myprinter output];
        [myprinter addData:@"Billy"];
        [myprinter addData:@"Peter"];
        [myprinter output];
        //创建一个Printer对象,当成Productable使用
        NSObject<Productable>* p = [[Printer alloc] init];
        //调用Productable协议中定义的方法
        NSLog(@"%@" , p.getProduceTime);
        //创建一个Printer对象,当成Output使用
        id<Output> out = [[Printer alloc] init];
        //调用Output协议中定义的方法
        [out addData:@"Tommy"];
        [out addData:@"Jimmy"];
        [out output];
    }
    return 0;
}

程序创建了一个Pointer对象,该对象包含上面三个协议的方法,因此可以调用。
如果程序使用协议来定义变量,那么这些变量只能调用该协议中声明的方法。
使用协议定义变量的语法:

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

正式协议与非正式协议的比较:

  • 非正式协议通过为NSObject创建类别来实现;而正式协议则直接使用@protocol创建。
  • 遵守非正式协议通过继承带特定类别的NSObject来实现;而遵守正式协议则必须实现协议中定义的方法。

为了弥补正式协议必须实现协议的所有方法造成的灵活性不足,OC 2.0新增了@optional、@required两个关键字:

  • @optional:位于该关键字之后、@optional或@end之前声明的方法是可选的。
  • @required:位于该关键字之后、@required或@end之前声明的方法是必需的,实现类必须实现这些方法。

示例:

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

通过在正式协议中使用@optional、@required关键字,正式协议可以完全代替非正式协议的功能。

协议与委托

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

以上是关于OC协议与委托的主要内容,如果未能解决你的问题,请参考以下文章

[OC学习笔记]协议与分类

IOS开发-OC学习-常用功能代码片段整理

OC第九节——协议与代理

OC基础 代理模式

GroovyMOP 元对象协议与元编程 ( 方法委托 | 使用 @Delegate 注解进行方法委托 )

GroovyMOP 元对象协议与元编程 ( 方法委托 | 批量方法委托 )