1、非正式协议
当某个类实现NSObject的该类别时,就需要实现该类别下的所有方法,这种基于NSObject定义的类别即可认为是非正式协议。
类别可以实现非正式协议,这种类别以NSObject为基础,为NSObject创建类别,创建类别时即可指定该类别应该新增的方法。
2、实例
1)定义以NSObject为基础的类别Eatable
#import <Foundation/Foundation.h>
// 以NSObject为基础定义Eatable类别
@interface NSObject (Eatable)
- (void) taste;
@end
2)NSObject派生子类FKApple
//.h
#import <Foundation/Foundation.h>
#import "NSObject+Eatable.h"
// 定义类的接口部分
@interface FKApple : NSObject
@end
//.m
#import "FKApple.h"
// 为FKApple提供实现部分
@implementation FKApple
- (void) taste
{
NSLog(@"苹果营养丰富,口味很好!");
}
@end
3)FKApple类的测试代码
#import <Foundation/Foundation.h>
#import "FKApple.h"
int main(int argc , char * argv[])
{
@autoreleasepool{
FKApple* app = [[FKApple alloc] init];
[app taste];
}
}
对于非正式协议,Object-C并不强制实现协议中得所有方法。
1、正式协议
基本语法
@protocol 协议名 <父协议1,父协议2>
{
//协议方法
}
- 协议名应与类名采用相同的命名规则
- 一个协议可以有多个直接父协议,但协议只能继承协议,不能继承类
- 协议中定义的方法只有方法签名,没有方法实现。协议中包含的方法即可是类方法,也可以是实例方法
- 协议里的所有方法都是公开的访问权限
2、实例
1)FKOutput协议
#import <Foundation/Foundation.h>
// 定义协议
@protocol FKOutput
// 定义协议的方法
@optional
- (void) output;
@required
- (void) addData: (NSString*) msg;
@end
2)FKProductable协议
#import <Foundation/Foundation.h>
// 定义协议
@protocol FKProductable
// 定义协议的方法
- (NSDate*) getProduceTime;
@end
3)FKPrintable协议,继承自上面两个协议
#import <Foundation/Foundation.h>
#import "FKOutput.h"
#import "FKProductable.h"
// 定义协议,继承了FKOutput、FKProductable两个协议
@protocol FKPrintable <FKOutput , FKProductable>
@required
// 定义协议的方法
- (NSString*) printColor;
@end
4)实现协议类FKPrinter
#import <Foundation/Foundation.h>
#import "FKPrintable.h"
// 定义类的接口部分,继承NSObject,遵守FKPrintable协议
@interface FKPrinter : NSObject <FKPrintable>
@end
#import "FKPrinter.h"
#define MAX_CACHE_LINE 10
// 为FKPrinter提供实现部分
@implementation FKPrinter
{
// 使用数组记录所有需要缓存的打印数据
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
5)测试代码
#import <Foundation/Foundation.h>
#import "FKPrinter.h"
int main(int argc , char * argv[])
{
@autoreleasepool{
// 创建FKPrinter对象
FKPrinter* printer = [[FKPrinter alloc] init];
// 调用FKPrinter对象的方法
[printer addData:@"疯狂iOS讲义"];
[printer addData:@"疯狂XML讲义"];
[printer output];
[printer addData:@"疯狂Android讲义"];
[printer addData:@"疯狂Ajax讲义"];
[printer output];
// 创建一个FKPrinter对象,当成FKProductable使用
NSObject<FKProductable>* p = [[FKPrinter alloc] init];
// 调用FKProductable协议中定义的方法
NSLog(@"%@" , p.getProduceTime);
// 创建一个FKPrinter对象,当成FKOutput使用
id<FKOutput> out = [[FKPrinter alloc] init];
// 调用FKOutput协议中定义的方法
[out addData:@"孙悟空"];
[out addData:@"猪八戒"];
[out output];
}
}
使用协议定义变量方法:
1)NSObject<协议1,协议2...>* 变量;
2)id<协议1,协议2...> 变量;
通过协议定义的变量,在编译时,仅仅只是通过协议所遵守的协议类型,因此只能调用该协议中定义的方法。
1、正式协议与非正式协议的区别:
- 非正式协议通过为NSObject创建类别来实现,而正式协议则直接使用@protocol创建
- 遵守非正式协议通过继承带特定类别的NSObject来实现,而遵守正式协议则有专门的Object-C语法
- 遵守非正式协议不要求实现协议中定义的所有方法,而遵守正式协议则必须实现协议中定义的所有方法
- 通过使用@optional和@required关键字,正式协议可以完全替代非正式协议
2、解决正式协议必须实现协议所有方法造成灵活性不足问题
- @optional:位于@optional关键字之后、@optional或@end之前声明的方法是可选的——实现类既可选择实现这些方法,也可不实现这些方法。
- @required:位于@required关键字之后,@required或@end之前声明的方法时必须得——实现类必须实现这些方法。
如下:
// 定义协议
@protocol FKOutput
// 定义协议的方法
@optional
- (void) output;
@required
- (void) addData: (NSString*) msg;
@end