Objective-C 【protocol-协议 的了解使用】

———————————————————————————————————————————
protocol(协议)

(1)引文:OC中的protocol和Java中的接口(interface)比较类似,Java中的接口(interface)就是一堆方法的声明(没有实现),而OC中的interface是一个类的头文件的声明,并不是真正意义上的接口,在OC中,接口是一个叫做 协议 的 protocol 来实现的。

和Java不同,protocol 可以声明一些必须实现的方法和一些选择实现的方法。(Java中如果要实现接口,就要必须实现接口中的所有方法,否则报错。         而protocol中声明的方法可以实现也可以不实现,这里其实默认也是全部都要实现的,只不过OC是弱语法语言,如果没有实现protocol中的方法,只会发出警告,而不会报错)

(2)OC中的协议(protocol):

①Protocol:就一个用途,用来声明一大堆的方法。(但是不能声明成员变量,也不能实现方法)
②只要某个类遵守了某个协议,就拥有了这个协议中的所有的  方法声明(实现还得自己去实现。如果是继承,则继承了父类的声明和实现,这和protocol是不同的)
③只要父类遵守了某个协议,那么子类也得遵守
④Protocol声明的方法可以让任何类去实现,Protocol就是协议
⑤OC不能继承多个类(遵守单继承原则),但是能够遵守多个协议。继承(:),遵守协议(< >)
⑥基协议:  <NSObject>  是基协议,是最根本最基本的协议,其中声明了很多最基本的方法。
⑦协议可以遵守协议,一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明。

(3)一般定义协议是在.h文件中,而谁采用这个协议就在谁的.m文件中实现方法。

①定义协议

@protocol 协议名称 <NSObject>
//方法声明列表
@end

注意:协议默认的要采纳NSObject的协议。

②采用协议

★★★采用协议的时候要引入协议的头文件,然后再让 类/协议 去遵守协议

★某个类遵守 某个协议 或者 某些协议,一个类可以遵守其他多个协议:

@interface 类名:父类 <协议名称1,协议名称2,协议名称3>
@end

★某个协议遵守 某个协议 或者 某些协议,一个协议可以遵守其他多个协议:(协议之间的继承关系)

@protocol 协议名称 <其他协议名称1,其他协议名称2,其他协议名称3>
@end

③实现协议中的方法

在类的.m文件中实现


接下来我们用一段代码来描述上述三个操作(①②③):

★main.m★

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

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Car *car1=[[Car alloc]init];
        
        [car1 run];
        
        [car1 train];
    }
    return 0;
}

★runProtocol.h★

#import <Foundation/Foundation.h>

@protocol runProtocol <NSObject>
//runProtocol协议遵守NSObject(基协议)

-(void)run;
-(void)jump;
-(void)jump2;
@end

★toolProtocol.h★

#import <Foundation/Foundation.h>
#import "runProtocol.h"  //我们引入之间创建的协议runProtocol的头文件

//协议遵守其他协议
@protocol toolProtocol <NSObject,runProtocol>  //然后让toolProtocol协议去遵守NSObject和runProtocol两个协议(这里注意,NSObject是每个协议创建的时候都要遵守的)

-(void)plane;
-(void)train;
@end

★Car.h★

#import <Foundation/Foundation.h>

#import "toolProtocol.h"
//#import "runProtocol.h"  //因为我们让toolProtocol协议去遵守runProtocol协议,所以只导入toolProtocol协议的头文件即可
//我们要引入我们遵守的协议的头文件

//类遵守协议
@interface Car : NSObject <toolProtocol>  //引入头文件后才能第二步遵守协议。这里也是只遵守toolProtocol协议就行了,这相当于让Car类同时遵守了toolProtocol和runProtocol两个协议。

@end

★Car.m★

#import "Car.h"

@implementation Car
//第三步才是实现协议中的方法

-(void)run
{
    NSLog(@"run!");
}

-(void)jump
{
    NSLog(@"jump!");
}

-(void)jump2
{
    NSLog(@"jump2!");
}

-(void)plane
{
    NSLog(@"plane!");
}

-(void)train
{
    NSLog(@"train!");
}

@end

(4)如果父类遵守一个协议,那么他的子类也相应遵守这个协议。

(5)NSObject是一个基类,同时也有NSObject这个基协议。(这个在上面的代码中已经提到)

★而且,查看源码得知:基类也是遵守基协议的。




———————————————————————————————————————————
protocol中的@required和@optional

@required和@optional 是协议方法声明中的两个至关重要的关键字,他们的作用主要控制方法是否必须要实现!

@required —— 必须实现(编译器默认是@required,若不实现,会警告)
@option —— 可以选择实现

用途在于程序员之间的交流。OC是弱语法,一旦没有写方法实现,能提醒别人知道。警告一下但是不报错,别人用你的代码的时候可以很好的提示一下。

这段知识比较简单,我用截图给大家说明一下:





我们创建了一个foodProtocol的协议,里面有@required属性的方法和@optional属性的方法。




我们让Dog类遵守foodProtocol协议。





我们选择实现其中一个@required类型的方法(drink),而其他的两个方法不实现,然后我们发现了一个警告。





警告显示eat方法在foodProtocol中声明了但是没有实现,但是却没有提示bark方法,显然原因大家已经知道了。

而这就是@required 和 @optional 关键字的用法。


———————————————————————————————————————————
protocol类型限制

(1)使用id存储对象时,对象的类型限制

格式:id <协议名称> 变量名;

如: id <Myprotocol> obj1;

(2)对象创建时类型限制

格式:类名 <协议名称> *变量名

如:Person <houseHold> *p= [ [ Student alloc ] init ];

(3)对象关联关系下,对象的类型限制

如:
@property (nonatomic,strong) id<girlFriend> girlfriend;
//创建一个girl的成员变量,表示创建出来的Person的实例对象应该拥有一个女友,而且女友需要遵守girlFriend的协议


代码:

★girlFriend★

#import <Foundation/Foundation.h>

@protocol girlFriend <NSObject>
//girlFriend必须遵守的(会做饭洗衣)
@required
-(void)cook;
-(void)washClothes;

//允许girlFriend不遵守的(有国企工作的优先)
@optional
-(void)goodJob;
@end

★Person.h★

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

@interface Person : NSObject

//创建一个girl的成员变量,表示创建出来的Person的实例对象应该拥有一个女友,而且女友需要遵守girlFriend的协议
//增加限制id<girlFriend>
//表示,id存储的对象必须实现了girlFriend协议
//找的女朋友,必须会洗衣做饭
@property (nonatomic,strong) id<girlFriend> girlfriend;

@end

★Person.m★

#import "Person.h"

@implementation Person

@end

★Girl.h★

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

@interface Girl : NSObject <girlFriend> //这里写上Girl类遵守girlFriend协议

@end

★Girl.m★

#import "Girl.h"

@implementation Girl
-(void)cook
{
    NSLog(@"cook dinner~");
}
-(void)washClothes
{
    NSLog(@"wash clothes~");
}
@end

★mian.m★

#import <Foundation/Foundation.h>
#import "Person.h"
#import "Girl.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Girl *girl1=[Girl new];
        
        Person *p1=[Person new];
        Person *p2=[Person new];
        
        p1.girlfriend=girl1;  //这里不会报错,因为girl1是Girl类型的变量,所以遵守girlFriend协议,可以赋值
        
        [p1.girlfriend cook];
        [p1.girlfriend washClothes];
        

        p1.girlfriend=p2;   //这里会报错,因为p2是Person类型的,而Person并不遵守这个协议,所以无法赋值

       


//        [p1.girlfriend cook];  //随便用一个p2来赋给p1,做p1的女朋友是不行的

    }
    return 0;
}


———————————————————————————————————————————
protocol的代理模式引入

MVC(三层框架)

m - model

v - view

c - controller

★代理模式概念:
传入的对象,代替当前类完成了某个功能,称为代理模式

代理模式应用场合:

①当对象A发生了一些行为,想告知对象B(让对象B成为对象A的代理对象)

②对象B想监听对象A的一些行为(让对象B成为对象A的代理对象)

③当对象A无法处理某些行为的时候(让对象B成为对象A的代理对象)

★我们可以看出,在上面的三个场合中,B需要满足协议要求的,才能帮助/代替A干某些事情。这就建立了一个代理模式应用场合。当然满足代理模式应用的场合还有许多。

下面是一个这样的程序:baby有吃东西和睡觉的方法,如果baby睡觉或者是吃饭都要去让一个人来帮助他完成(在吃东西和睡觉方法中调用另外一个对象的方法),而保姆就是那个具备照顾baby要求的人选(遵守了协议)。现在我们要求一个老师去照顾baby,帮助baby去完成吃饭睡觉的工作,但老师是不遵守协议的,然后我们要让老师也遵循这个协议去照顾baby。

形容的实在太啰嗦了,原谅我的口才不是太好。

问题的关键先建立模型,将方法写好之后再考虑协议的问题。一步一步的添加上述要求。

代码:

//  babysitProtocol.h

#import <Foundation/Foundation.h>
@class Baby;
@protocol babysitProtocol <NSObject>
-(void)feedBaby:(Baby *)baby;
-(void)hongBabyToSleep:(Baby *)baby;
@end

//  Baby.h

@interface Baby : NSObject
@property (nonatomic) id <babysitProtocol> bm;
@property (nonatomic) float weight;
@property (nonatomic) float height;

-(void)wantEat;
-(void)wantSleep;
@end

//  Baby.m

#import "Baby.h"
#import "Baomu.h"
@implementation Baby
-(void)wantEat
{
    NSLog(@"baby want eat!");
    [self.bm feedBaby:self];
}
-(void)wantSleep
{
    NSLog(@"baby want sleep!");
    [self.bm hongBabyToSleep:self];
}
@end

//  Baomu.h

#import <Foundation/Foundation.h>
#import "babysitProtocol.h"
@class Baby;
@interface Baomu : NSObject <babysitProtocol>
-(void)feedBaby:(Baby *)baby;
-(void)hongBabyToSleep:(Baby *)baby;
@end

//  Baomu.m

#import "Baomu.h"
#import "Baby.h"
@implementation Baomu
-(void)feedBaby:(Baby *)baby
{
    NSLog(@"Baomu feed baby!");
    baby.weight+=1;
}
-(void)hongBabyToSleep:(Baby *)baby
{
    NSLog(@"Baomu baby sleep!");
    baby.height+=0.5;
}
@end

//  Teacher.h

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

@interface Teacher : NSObject <babysitProtocol>

@end

//  Teacher.m

#import "Teacher.h"
#import "Baby.h"
@implementation Teacher
-(void)feedBaby:(Baby *)baby
{
    NSLog(@"Teacher feed baby!");
    baby.weight+=1;
}
-(void)hongBabyToSleep:(Baby *)baby
{
    NSLog(@"Teacher baby sleep!");
    baby.height+=0.5;
}
@end

//  main.m

//一般而言什么 类/协议 遵守另外的协议,后面的协议尖括号是紧跟着 类/协议 去写的,不要将协议的位置写错
#import <Foundation/Foundation.h>
#import "Baby.h"
#import "Baomu.h"
#import "Teacher.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Baby *baby1=[Baby new];
        Baomu *baomu1=[Baomu new];
        
        baby1.bm=baomu1;
        
        [baby1 wantEat];
        //以上四句话是非常合理的,因为baomu1是Baomu的实例对象,baomu1遵守babysitProtocol协议,所以说baby1可以拥有他,并且调用。
        
        //但是如果写下面两句显然就有错了,因为我们并没有让Teacher类去遵守babysitProtocol这个协议。
//        Teacher *teacher1=[Teacher new];
//        baby1.bm=teacher1;
        //那么我们应该怎么改进呢?
//        显然,我们应该让Teacher类也遵守babysitProtocol协议,然后将Baby中的bm属性设置为id类型,这样Teacher也能feedBaby和hongBabyToSleep了~
//        具体代码在各个文件中已经改变~
        
        Baby *baby2=[Baby new];
        Teacher *teacher2=[Teacher new];
        
        baby2.bm=teacher2;
        
        [baby2 wantSleep];
    }
    return 0;
}


———————————————————————————————————————————

版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/wzy294250051/p/4787876.html