OC中的协议
协议的理论理解
OC语言是单继承多协议的语言。在学习继承的同时,还需要学会适当的使用协议完成特定的功能。
协议和我们生活中的含义大同小异,例如:有一个孩子,由于年龄很小,无法照顾自己,所以不得不委托他人来照顾他的饮食起居。
从协议的角度分析上面的例子,孩子需要得到照顾,但是他自己办不了,只能让保姆照顾他。所以孩子是协议生成方,保姆是协议实现方。
从程度的角度上考虑,协议就是某类创建,其他类来帮其实现。
使用协议需要注意的点
1、协议没有父类(协议可以服从多个(>=0)协议(一般服从NSObject协议)。
(这里的NSObject是NSObject.h文件中的定义的一个协议,并不是NSObject这个类,如果有疑问,大家可以command点击进入NSObject这个协议,顺便了解一下这个协议中所包含的方法)
2、协议中不能定义变量(属性),只能定义方法。
协议的创建
1、创建一个OC工程,例如:Demo2,
2、在此工程中添加一个Children类,
3、然后再添加一个Nanny(保姆)类,
3、在Children这个类中添加一个协议,代码如下;
Children.h文件:
@protocol是定义协议的关键字,告诉编译器,要开始声明协议了。
ChildrenDelegate是协议的名字,规范命名为:类名+Delegate。
<NSObject>是一个协议,其中提供了很多方法供我们使用,通过main.m中代码的解释我们会看到服从这个协议的好处。(协议用“<>”)
@end告诉编译器我的协议声明完了。
在协议中,有几个方法的修饰词供我们使用:
@required(表示以下方法必须实现)
@optional(表示以下方法可以不实现)
如果不使用任何关键字修饰,默认情况下是@required。
例如在这个协议中:
我们还可以向协议中添加带有@required和@optional关键字修饰的方法:
@required
-(void)takeCare;
这个方法用@required修饰,所以是协议实现方必须实现的方法。
@optional
-(void)cook;
这个方法用@optional修饰,表明协议实现方可以选择是否实现这个协议。
第6行代码--delegate的创建
之前讲过,协议是需要让其他人来帮你实现的,那么就产生了一个问题:怎么规定这个帮你实现方法的人是谁呢?
为了解决这个问题,需要声明这么一个属性来确定谁是代理实现方。
@property是定义属性的关键字。
nonatomic表明这个属性是非原子性的,加快了运行效率;weak是弱引用,是使用协议时用到的属性。
id:前面提到,可以表示任何数据类型,使用id 的原因是无法确定谁会成为这个代理的实现方。
<ChildrenDelegate>表明这个属性方法和这个协议相关联。
delegete:是代理属性名(代理属性名的命名也是有潜规则的,一般命名为delegate,表明这是一个代理属性。还有一个原因就是因为Foundation框架中的代理属性也是这么命名的,所以我们为了让自己变得专业,也要和专业开发人员学习。如果一个类中还需要一个代理,就命名为dataSource),一般一个类最多也就有两个协议。
通过上面的工作,我们在Children这个文件中,定义了一个协议,并将这个协议和我们的类进行关联(由于我们在类中声明了和协议相关联的属性,所以使整个类都和协议产生了关联,如果有些疑惑,下面的代码可能会帮助到你)。
协议的实际应用
下面我们在nanny这个类中实现这个协议,首先来看代码:
Nanny.h文件中的代码:
<ChildrenDelegate>表明这个类服从ChildrenDelegate这个协议(<>中还可以添加多个协议,中间用逗号隔开。)
如果一个类服从一个协议,那么这个类就要实现协议中的方法。
Nanny.m文件中的代码:
通过前面的学习,完成了协议声明方和实现方的代码实现,下面学习使用协议来完成一定功能。
main.m中的代码实现:
the nanny is taking care of the child eating something!
代码讲解:
首先,要运用协议,我们要引入代理声明方和实现方。(为什么呢?只引入实现方不行吗?你会发现,是可行的。能这样做,是因为在Nanny类中引入了Children类,所以.m中只需引入Nanny类就可以了。
之所以要重复引入这个类,是因为这样增强了代码的可读性,而且由于@import的特性(不管引入多少次,一个类只引入一次),并没有降低代码的运行效率)。
在5-7行代码中,首先声明了两个类的对象,这是为第7行做准备,由于代理声明方要确定代理实现方是哪个对象,所以要使用到之前使用到的代理属性。所以使用点语法来为代理属性进行赋值。
为什么赋值的是类对象呢?这个很好理解,首先别人要帮你完成任务,首先它必须是一个实体,也就是对象。
8-10行代码是要重点理解的,child.delegete其实就是nanny对象,那么为什么不直接使用nanny呢?在简单程序中是可以的,如果在复杂的程序中,使用实现方对象的前提是你必须获得这个对象,往往费时费力。所以为了避免这种情况,一般使用上面这种方式,而且代码读起来非常清晰,易于理解。
respondsToSelector:方法:这个方法是NSObject协议中的方法,功能是判断代理实现方是否已经实现了代理中的某个方法。
使用这个方法的好处就是,如果代理实现方没有实现这个代理方法,整个工程不至于崩溃(如果不使用这个方法做判断,一旦你调用了只声明没有实现的方法,程序将会出错。这也就解释了为什么创建协议的时候,默认至少该协议要服从NSObject这个协议了)。
@selector():专门在调用方法时用到,如果你看到某个方法的参数是SEL类型时,就说明这需要传进去一个方法,书写语法为:@selector(方法)(这里的方法是简写的形式,就是不显示参数名)。
结合上面的解释,总体的来解释这个if语句,如果代理实现方实现了eat这个代理方法,那么就执行[child.delegete eat],也就是调用nanny对象的eat方法。
那么,问题就来了, eat方法没有在Nanny.h接口文件中声明,只是在.m文件中做了实现。为什么外界能调用呢?
这和协议有关系,一旦某个类服从了某个协议,那么该类就需要实现协议中规定必须实现的方法。而这些方法可以通过[协议生成方.delegate 方法]的方式调用,
协议可以实现的功能
明白协议的代码编写规则之后,还需要了解协议在实际开发过程中的作用。由于协议本身在本文件中声明,而其方法却在其他类文件中实现的这一特性,可以进行两个类之间的数据传递(实现的具体方式就是将数据传递给协议中的方法参数,通过方法的“异地”执行,将数据传递过去)。
OC语言是单继承多协议的语言。在学习继承的同时,还需要学会适当的使用协议完成特定的功能。
协议和我们生活中的含义大同小异,例如:有一个孩子,由于年龄很小,无法照顾自己,所以不得不委托他人来照顾他的饮食起居。
从协议的角度分析上面的例子,孩子需要得到照顾,但是他自己办不了,只能让保姆照顾他。所以孩子是协议生成方,保姆是协议实现方。
从程度的角度上考虑,协议就是某类创建,其他类来帮其实现。
使用协议需要注意的点
1、协议没有父类(协议可以服从多个(>=0)协议(一般服从NSObject协议)。
(这里的NSObject是NSObject.h文件中的定义的一个协议,并不是NSObject这个类,如果有疑问,大家可以command点击进入NSObject这个协议,顺便了解一下这个协议中所包含的方法)
2、协议中不能定义变量(属性),只能定义方法。
协议的创建
1、创建一个OC工程,例如:Demo2,
2、在此工程中添加一个Children类,
3、然后再添加一个Nanny(保姆)类,
3、在Children这个类中添加一个协议,代码如下;
Children.h文件:
#import <Foundation/Foundation.h> @protocol ChildrenDelegate <NSObject>//协议声明 -(void)eat; @end @interface Children : NSObject @property(nonatomic,weak)id<ChildrenDelegate>delegete; @end2-4行代码--协议的生成部分:
@protocol是定义协议的关键字,告诉编译器,要开始声明协议了。
ChildrenDelegate是协议的名字,规范命名为:类名+Delegate。
<NSObject>是一个协议,其中提供了很多方法供我们使用,通过main.m中代码的解释我们会看到服从这个协议的好处。(协议用“<>”)
@end告诉编译器我的协议声明完了。
在协议中,有几个方法的修饰词供我们使用:
@required(表示以下方法必须实现)
@optional(表示以下方法可以不实现)
如果不使用任何关键字修饰,默认情况下是@required。
例如在这个协议中:
-(void)eat;
这个方法没有用关键字修饰,编译器默认情况下,认为它是必须实现的方法。我们还可以向协议中添加带有@required和@optional关键字修饰的方法:
@required
-(void)takeCare;
这个方法用@required修饰,所以是协议实现方必须实现的方法。
@optional
-(void)cook;
这个方法用@optional修饰,表明协议实现方可以选择是否实现这个协议。
第6行代码--delegate的创建
之前讲过,协议是需要让其他人来帮你实现的,那么就产生了一个问题:怎么规定这个帮你实现方法的人是谁呢?
为了解决这个问题,需要声明这么一个属性来确定谁是代理实现方。
@property是定义属性的关键字。
nonatomic表明这个属性是非原子性的,加快了运行效率;weak是弱引用,是使用协议时用到的属性。
id:前面提到,可以表示任何数据类型,使用id 的原因是无法确定谁会成为这个代理的实现方。
<ChildrenDelegate>表明这个属性方法和这个协议相关联。
delegete:是代理属性名(代理属性名的命名也是有潜规则的,一般命名为delegate,表明这是一个代理属性。还有一个原因就是因为Foundation框架中的代理属性也是这么命名的,所以我们为了让自己变得专业,也要和专业开发人员学习。如果一个类中还需要一个代理,就命名为dataSource),一般一个类最多也就有两个协议。
通过上面的工作,我们在Children这个文件中,定义了一个协议,并将这个协议和我们的类进行关联(由于我们在类中声明了和协议相关联的属性,所以使整个类都和协议产生了关联,如果有些疑惑,下面的代码可能会帮助到你)。
协议的实际应用
下面我们在nanny这个类中实现这个协议,首先来看代码:
Nanny.h文件中的代码:
#import <Foundation/Foundation.h> #import "Children.h" @interface Nanny : NSObject<ChildrenDelegate> @end代码解释:
<ChildrenDelegate>表明这个类服从ChildrenDelegate这个协议(<>中还可以添加多个协议,中间用逗号隔开。)
如果一个类服从一个协议,那么这个类就要实现协议中的方法。
Nanny.m文件中的代码:
#import "Nanny.h" @implementation Nanny -(void)eat{ NSLog(@"the nanny is taking care of the child eating something!"); } @end协议的具体应用
通过前面的学习,完成了协议声明方和实现方的代码实现,下面学习使用协议来完成一定功能。
main.m中的代码实现:
#import <Foundation/Foundation.h> #import "Children.h" #import "Nanny.h" int main(int argc, const char * argv[]) { Children * child=[[Children alloc] init]; Nanny * nanny=[[Nanny alloc] init]; child.delegete=nanny; if ([child.delegete respondsToSelector:@selector(eat)]) { [child.delegete eat]; } return 0; }输出结果:
the nanny is taking care of the child eating something!
代码讲解:
首先,要运用协议,我们要引入代理声明方和实现方。(为什么呢?只引入实现方不行吗?你会发现,是可行的。能这样做,是因为在Nanny类中引入了Children类,所以.m中只需引入Nanny类就可以了。
之所以要重复引入这个类,是因为这样增强了代码的可读性,而且由于@import的特性(不管引入多少次,一个类只引入一次),并没有降低代码的运行效率)。
在5-7行代码中,首先声明了两个类的对象,这是为第7行做准备,由于代理声明方要确定代理实现方是哪个对象,所以要使用到之前使用到的代理属性。所以使用点语法来为代理属性进行赋值。
为什么赋值的是类对象呢?这个很好理解,首先别人要帮你完成任务,首先它必须是一个实体,也就是对象。
8-10行代码是要重点理解的,child.delegete其实就是nanny对象,那么为什么不直接使用nanny呢?在简单程序中是可以的,如果在复杂的程序中,使用实现方对象的前提是你必须获得这个对象,往往费时费力。所以为了避免这种情况,一般使用上面这种方式,而且代码读起来非常清晰,易于理解。
respondsToSelector:方法:这个方法是NSObject协议中的方法,功能是判断代理实现方是否已经实现了代理中的某个方法。
使用这个方法的好处就是,如果代理实现方没有实现这个代理方法,整个工程不至于崩溃(如果不使用这个方法做判断,一旦你调用了只声明没有实现的方法,程序将会出错。这也就解释了为什么创建协议的时候,默认至少该协议要服从NSObject这个协议了)。
@selector():专门在调用方法时用到,如果你看到某个方法的参数是SEL类型时,就说明这需要传进去一个方法,书写语法为:@selector(方法)(这里的方法是简写的形式,就是不显示参数名)。
结合上面的解释,总体的来解释这个if语句,如果代理实现方实现了eat这个代理方法,那么就执行[child.delegete eat],也就是调用nanny对象的eat方法。
那么,问题就来了, eat方法没有在Nanny.h接口文件中声明,只是在.m文件中做了实现。为什么外界能调用呢?
这和协议有关系,一旦某个类服从了某个协议,那么该类就需要实现协议中规定必须实现的方法。而这些方法可以通过[协议生成方.delegate 方法]的方式调用,
协议可以实现的功能
明白协议的代码编写规则之后,还需要了解协议在实际开发过程中的作用。由于协议本身在本文件中声明,而其方法却在其他类文件中实现的这一特性,可以进行两个类之间的数据传递(实现的具体方式就是将数据传递给协议中的方法参数,通过方法的“异地”执行,将数据传递过去)。