【IOS 开发学习总结-OC-20】★★★objective-c面向对象——协议(protocol)
2015-09-26 10:53
447 查看
协议(protocol)的作用类似于其他语言的接口,用于定义多个类应该遵守的规范。
协议定义了某些类所需遵守的规范,它不关心这些类的内部状态数据和实现细节,只规定这些类必须提供某些方法。提供你呢这些方法的类就可满足实际需要。
协议不提供任何实现,通常是定义一组公用方法。方法的实现交给类来完成。
示例程序:
FKApple.h
FKApple.m
NSObject +Eatable.h
EatableTest.m
运行结果:
特别提示:
对于实现非正式协议的类而言,objective-c 并 不强制实现该协议中的所有方法,也就是说,上面的 FKApple 类也可以 不实现 taste 方法。
但是,如果 FKApple 类不实现 taste 方法,且非正式协议本身也没有实现该方法,运行该程序就会引起错误。
错误信息如下:
注意:虽然 OC 编译器并不强制遵守非正式协议的类必须实现该协议中所有的方法,,但如果该类没有实现协议中的 某个方法,那么程序运行时如果调用该方法,就会引发 unrecognized selector 错误。
定义正式协议的基本语法格式如下:
语法说明:
1. 协议名应与类名采用相同的命名规则。——由多个有意义的单词连接而成,每个单词首字母大写,单词间无需任何分隔符。
2. 一个协议可以有多个直接父协议,,但协议只能继承协议,不能继承类。
3. 协议中 定义的方法中只有方法签名,没有方法实现。协议中包含的方法可以是类方法,也可以是实例方法。
4. 协议里所有的方法都是公开的访问权限——协议定义的是多个类共同单单公共行为规范。
5. 协议的继承与类的继承不一样,协议支持多继承——一个协议可以有多个直接的 父协议。与类的继承相似的是:子协议继承多个父协议,将会获得父协议里定义的所有方法。
6. 一个协议继承多个父协议时,多个父协议排在<>之间,彼此间以英文逗号隔开。
一个类可以同时遵守多个协议。
示例代码:
FKOutput.h
FKProductable.h
FKPrintable.h
FKPrinter.h
FKPrinter.m
如果实现类实现了协议 中所有的方法,这样程序就可以调用该实现类所实现的方法。测试代码如:
FKPrinterTest.m
运行结果:
我们看到上面最后一段代码中变量 p 和 out 是使用协议来定义的变量。那么这些变量只能调用该协议中声明的方法,否则编译器 会提示错误。
用协议来定义变量的语法格式有2种:
1.
2.
通过上面的语法格式定义的变量,它们的编译时类型仅仅只是所遵守的协议类型,因此只能调用该协议中定义的方法。
[thead]
通过在正式协议中使用@optional,@required关键字,正式协议完全可以代替非正式协议的功能。
2个关键字作用如下:
- @optional:位于该关键字后,@required或@end之前声明的方法是可选的——实现类既可以选择实现这些方法,也可以不实现这些方法
- @required:位于该关键字后,@optional或@end之前声明的方法是必须实现的。如果没有实现这些方法,编译器会提示警告。@required是默认的行为。
协议定义了某些类所需遵守的规范,它不关心这些类的内部状态数据和实现细节,只规定这些类必须提供某些方法。提供你呢这些方法的类就可满足实际需要。
协议不提供任何实现
协议体现的是规范和实现分离的设计哲学。让规范与实现分离是一种松耦合设计,这正是协议的好处。协议不提供任何实现,通常是定义一组公用方法。方法的实现交给类来完成。
使用类别实现非正式协议
为 NSObject 创建类别,创建类别时,就需要实现该类别下的所有方法。这种基于NSObject定义的类别即可认为是非正式协议。示例程序:
FKApple.h
#import <Foundation/Foundation.h> #import "NSObject+Eatable.h" // 定义类的接口部分 @interface FKApple : NSObject @end
FKApple.m
#import "FKApple.h" // 为FKApple提供实现部分 @implementation FKApple - (void) taste { NSLog(@"苹果营养丰富,口味很好!"); } @end
NSObject +Eatable.h
#import <Foundation/Foundation.h> // 以NSObject为基础定义Eatable类别 @interface NSObject (Eatable) - (void) taste; @end
EatableTest.m
#import <Foundation/Foundation.h> #import "FKApple.h" int main(int argc , char * argv[]) { @autoreleasepool{ FKApple* app = [[FKApple alloc] init]; [app taste]; } }
运行结果:
2015-09-26 11:31:18.230 923[1709:72468] 苹果营养丰富,口味很好!
特别提示:
对于实现非正式协议的类而言,objective-c 并 不强制实现该协议中的所有方法,也就是说,上面的 FKApple 类也可以 不实现 taste 方法。
但是,如果 FKApple 类不实现 taste 方法,且非正式协议本身也没有实现该方法,运行该程序就会引起错误。
错误信息如下:
2015-09-26 12:23:47.369 923[2190:88420] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[FKApple taste]: unrecognized selector sent to instance 0x78e0a930'
注意:虽然 OC 编译器并不强制遵守非正式协议的类必须实现该协议中所有的方法,,但如果该类没有实现协议中的 某个方法,那么程序运行时如果调用该方法,就会引发 unrecognized selector 错误。
正式协议
定义正式协议
和定义类不同,正式协议使用@protocol 关键字。定义正式协议的基本语法格式如下:
@protocol 协议名<父协议 1,父协议2> { 0到多个方法定义.... }
语法说明:
1. 协议名应与类名采用相同的命名规则。——由多个有意义的单词连接而成,每个单词首字母大写,单词间无需任何分隔符。
2. 一个协议可以有多个直接父协议,,但协议只能继承协议,不能继承类。
3. 协议中 定义的方法中只有方法签名,没有方法实现。协议中包含的方法可以是类方法,也可以是实例方法。
4. 协议里所有的方法都是公开的访问权限——协议定义的是多个类共同单单公共行为规范。
5. 协议的继承与类的继承不一样,协议支持多继承——一个协议可以有多个直接的 父协议。与类的继承相似的是:子协议继承多个父协议,将会获得父协议里定义的所有方法。
6. 一个协议继承多个父协议时,多个父协议排在<>之间,彼此间以英文逗号隔开。
实现(遵守)协议
在类定义的接口部分可指定该类继承的父类,以及遵守的协议 。语法如下:@interface 类名:父类<协议1,协议2...>
一个类可以同时遵守多个协议。
示例代码:
FKOutput.h
#import <Foundation/Foundation.h> // 定义协议 @protocol FKOutput // 定义协议的方法 @optional - (void) output; @required - (void) addData: (NSString*) msg; @end
FKProductable.h
#import <Foundation/Foundation.h> // 定义协议 @protocol FKProductable // 定义协议的方法 - (NSDate*) getProduceTime; @end
FKPrintable.h
#import <Foundation/Foundation.h> #import "FKOutput.h" #import "FKProductable.h" // 定义协议,继承了FKOutput、FKProductable两个协议 @protocol FKPrintable <FKOutput , FKProductable> @required // 定义协议的方法 - (NSString*) printColor; @end
FKPrinter.h
#import <Foundation/Foundation.h> #import "FKPrintable.h" // 定义类的接口部分,继承NSObject,遵守FKPrintable协议 @interface FKPrinter : NSObject <FKPrintable> @end
FKPrinter.m
#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
如果实现类实现了协议 中所有的方法,这样程序就可以调用该实现类所实现的方法。测试代码如:
FKPrinterTest.m
#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]; } }
运行结果:
2015-09-26 13:52:03.357 923[2671:111493] 打印机使用红色打印:疯狂iOS讲义 2015-09-26 13:52:03.359 923[2671:111493] 打印机使用红色打印:疯狂XML讲义 2015-09-26 13:52:03.359 923[2671:111493] 打印机使用红色打印:疯狂Android讲义 2015-09-26 13:52:03.360 923[2671:111493] 打印机使用红色打印:疯狂Ajax讲义 2015-09-26 13:52:03.367 923[2671:111493] 2015-09-26 05:52:03 +0000 2015-09-26 13:52:03.368 923[2671:111493] 打印机使用红色打印:孙悟空 2015-09-26 13:52:03.368 923[2671:111493] 打印机使用红色打印:猪八戒
我们看到上面最后一段代码中变量 p 和 out 是使用协议来定义的变量。那么这些变量只能调用该协议中声明的方法,否则编译器 会提示错误。
用协议来定义变量的语法
那么用协议来定义变量的语法是怎样的呢?用协议来定义变量的语法格式有2种:
1.
NSObject <协议1,协议2..>* 变量;
2.
id < 协议1,协议2...> 变量;
通过上面的语法格式定义的变量,它们的编译时类型仅仅只是所遵守的协议类型,因此只能调用该协议中定义的方法。
正式协议与非正式协议的区别
区别 | 非正式协议 | 正式协议 |
---|---|---|
1 | 通过为 NSObject创建类别来实现 | 直接使用@protocol 创建 |
2 | 遵守(实现)非正式协议通过继承带特定类别的 NSObject 来实现 | 遵守(实现)正式协议必须实现协议中定义的所有方法 |
3 | 遵守(实现)非正式协议不需实现协议中定义的所有方法 | 遵守(实现)正式协议必须实现定义中所有的方法 |
正式协议为提高灵活性增加的关键字
为了弥补遵守正式协议必须实现协议的所有方法造成的灵活性不足,OC增加了2个关键字:@optional,@required。通过在正式协议中使用@optional,@required关键字,正式协议完全可以代替非正式协议的功能。
2个关键字作用如下:
- @optional:位于该关键字后,@required或@end之前声明的方法是可选的——实现类既可以选择实现这些方法,也可以不实现这些方法
- @required:位于该关键字后,@optional或@end之前声明的方法是必须实现的。如果没有实现这些方法,编译器会提示警告。@required是默认的行为。
相关文章推荐
- org.springframework.orm.hibernate3.HibernateSystemException: a different object with the same identi
- 【IOS 开发学习总结-OC-19】★★objective-c面向对象之——类别与扩展
- NSObject的描述(+description)方法和对象的描述(-description)方法,NSLog默认输出的修改(后知后觉)
- Objective-C 基础知识之 (十八):Copy NSString的Copy现象
- Objective-C 基础知识之 (十七):内存管理原则二
- Objective-C 基础知识之 (十六):内存管理原则一
- Objective-C 基础知识之 (十五):KVC
- Objective-C 基础知识之 (十四):属性 Property 点语法
- Objective-C 基础知识之(十三):类目、延展、协议、代理等的基本概念
- Objective-C 基础知识之(十二):NSDate
- Objective-C 基础知识之(十一):Block
- iOS经典讲解之Objective-C内存管理基础知识及内存管理黄金原则
- JDK源码阅读之Object类
- 《Objective-C入门 》 第一篇 - 合成存取器
- core foundation与objective-c之间有什么关系和区别
- Objective-C:为何使用ivar成员变量?Why would you use an ivar?[zz]
- Swift - AnyObject与Any的区别
- Swift调用Objective-C编写的代码(颜色选择器KKColorListPicker调用)
- Objective-C和JS交互
- C++ 11 - STL - 函数对象(Function Object) (中)