OC-Protocol

6-19 协议

一.协议


1.协议作用:完成两个类的对象的通信。

------------------------------------------------------------------------------------

2.定义方法:

例子:

@protocol 协议名 <遵守的其他协议的名称>


方法声明;


@end


注意:a.以上内容可以写在任意位置,但如同类一样,一般都写在一个头文件里。

         b.协议的内容只能有方法声明,不能有变量声明 

------------------------------------------------------------------------------------

3.如何让类签署(or称遵循)某协议呢?

答:1)遵守协议的某个类里,要包含协议的头文件。这时就代表该类签署了该协议,且必须实现协议里的一些方法。(协议中的方法,类中可以不用再次声明。)

     2)在类的声明中,要加入<协议名>,当有多个要遵守的协议时,协议名之间用逗号","隔开。

        例子:

              @interface Coder : NSObject <Program//程序员类遵守了Program协议

------------------------------------------------------------------------------------

4.如何让已经遵循某协议的类去调用协议里的方法(即:使用代理)呢?

答:

        前提说明:Coder类遵守了Program协议;Program协议中有program方法。

        

       主要函数:  id <Program> delegate = [tom retain];


        例子:

                 /*  有个程序员会编程,他把这个能力出卖给了协议,然后作为代理去编程*/

        Coder * tom = [[Coder alloc]init];//一个类遵守了协议,那么这个类的也就遵守了协议

        //设置代理:id 指向的对象,要遵守program协议;

        id <Program> delegate = [tom retain];//把程序员Tom作为一个代理,(Tom指向的对象计数器+1,然后把对象地址赋值给delegate).

        [delegate program];

        [delegate release];

        [tom release];


   注意:a.代理只能调用协议里的方法

        b.当代理没有调用协议里的方法是,有警告,有语法错误,尽量不要这样用,但也可以执行。

-----------------------------------------------------------------------------------------------------------------------------------

5.协议的属性: @required  &  @optional


@required:修饰的方法,遵循这个协议的类必须要实现,这个属性是协议的默认属性,可以不写这个关键字。


@optional:修饰的方法是可选的,遵循这个协议的类可以实现,也可以不实现。也就是说,如果遵守协议的类,有这个方法,那么就出卖,没有就什么都不做。

------------------------------------------------------------------------------------

6.关于继承

我们可以看到,创建一个协议的时候,它会自动遵循<NSObject>协议,但我们自己并没有<NSObject>协议里的方法,这是如何实现的呢?

答:以自定义WorkerConform协议,Coder类遵循WorkerConform协议为例。

虽然WorkerConform遵守NSObject协议,但由于NSObject类中,已经实现了NSObject协议中的方法,而且Coder类继承于NSObject类,那么就代表Coder类实现了NSObject协议中的方法。

------------------------------------------------------------------------------------

7.单向代理

------代理本质:就是一个对象指针,只不过是只能执行协议中的内容


------何时使用代理,如何设置代理?

:情况一:一个事物A委托另外一个事物B

              则:A是主动方;

        B是被动方;B设置为代理。

              

   情况二:一个事物AB发送消息,让B做某些事

       :B是代理。


------单向代理的具体实例:见老师orMy课堂代码OC4

------程序说明:

有一个公司的老板,boss zhang,不会买木头;

 worker wang 会买木头;

 boss zhang 通知 worker wang去买木头;

 <==>boss zhang 委托 worker wang 去买木头

 委托 == 代理;

------实现思路:

1)设计两个类,Boss类和Worker;设计一个协议BuyWood协议。

2)BuyWood协议中:声明方法-(void)buyWood:(int)_w;//买多少木头。

3)Worker类遵循BuyWood协议,实现买木头方法。

4)Boss:a.头文件要包含BuyWood协议的头文件。

         b.定义成员变量:id <BuyWood> delegate;//把遵守买木头协议的worker作为代理

         c.因为成员变量(delegate)本质是对象指针,因此它的setter函数要特殊处理。

                  ------手动实现方法:

                          -(void)setDelggate:(id<BuyWood>)_delegate

            {

               if(delegate!= _delegate)//先判断新值旧值是否一致

               {

                 [delegate release];//release旧值

                 delegate = [_delegate retain]; //再赋新值

               }

            }

        ------自动实现方法:

           @property (retain)id <BuyWood>delegate;

            @synthesize delegate;


        ------注意:delegate是成员变量,成员变量在整个boss消失的时候才会消失,又因为这里为delegate retain 了一次,那么必须要在deallocrelease一次。因此要重写dealloc方法。

                 ------重写dealloc方法:

                            -(void)dealloc

           {

               [delegate release];

               [super dealloc];

               NSLog(@"boss木有了!");

           }

          d.要写一个给代理发送消息的方法:Boss通知Worker买木头。

                      -(void)sendBuyWood:(int)_w

                      {

                          NSLog(@"send buy wood");

                          [delegate buyWood:_w];

                      }

5)主函数的实现:

                       Worker * wang = [[Worker alloc]init];

           Boss * zhang = [[Boss alloc]init];

     

           [zhang setDelggate:wang];//wang设置为代理

           [zhang sendBuyWood:10];

           [wang release];

           [zhang release];

------------------------------------------------------------------------------------

8.双向代理:互相委托对方做一些事情。

------程序说明:

小狗算数:

                  委托

 小狗    一次 <----------  驯兽师 挥手

                  委托

               ---------->       记一次数

------错误设计思路:

按照单向代理的方式,相互将对方设置为代理。

造成后果:两个类的对象最后都没有得到释放。(具体代码OC6-error)


-------什么是相互引用,如何进行相互引用?

:相互引用:当两个类,把对方的对象指针互作为成员变量,设置或赋值时,就会引起相互引用。

   涉及到相互引用,那么必须一强一弱

   强引用:会使计数器加1,或者置为1(retain alloc) 对应@property的参数(retain/copy/strong)

   弱引用:直接赋值操作,其他什么都不操作。(不会修改计数器的值)对应@property的参数(assign)

   双向代理:就是一个相互引用/循环引用


-------正确设计思路:

:采用一强一弱的设计方式。具体实例见(OC6-OK)

------------------------------------------------------------------------------------

9.代理的常用写法

步骤如下:

a.确定主动方与被动方(被动方为代理,需要遵守协议)

b.将协议写在主动方的头文件中

c.主动方类的实现:

  -主动方的对象指针采用强引用来实现。

     -成员变量中,要有一个代理。

  -实现向被动方发消息的方法。

d.被动方类的实现:

  -遵守协议

  -成员变量中,要有一个主动方的一个对象指针。

  -代理采用弱引用来实现。

     -实现主动方发来消息后,被动方要实现的动作。

     

注意:这里主动方强引用&被动方弱引用,实现的方法,都是在对方类中用到,在对方类中实现的,不要写反了。


   具体实例见OC7

********************************************************************************************

二.其他知识点补充


1.头文件相互引用问题

-----什么是头文件相互引用?

:例如:ClassA的头文件中写了#import"ClassB";同时,在ClassB的头文件中写了#import"ClassA",这种情况就是头文件相互引用。


-----会造成什么后果?

:两个类都没有创建成功。


-----如何解决这个问题?

:#import,#include都不能解决这个问题。要使用向前引用声明一个类来解决。

   例如:ClassA的头文件中,写@class ClassB;它表明:提前声明了ClassB类,假设这个类已经实现了,下面的函数可以使用这个类;(意思是在这个头文件中只能使用这个类名,因为头文件中都是函数声明,所以只能是使用了这个ClassB的类名,不会使用其他东西,其他的使用,在ClassA的实现中使用)

                 接下来,ClassA.m文件中,写#import"ClassB.h",这样,ClassA的实现中,就可以使用ClassB里的方法了。

        按照上种方式进行编程,没有头文件相互引用,解决了这个问题。在OC中,不可以写出头文件相互引用的形式。


------------------------------------------------------------------------------------

2.关于@property的属性nonatomic


我们已经知道,当一个类的成员变量是对象指针时,在@property声明时,要加入"retain"这个关键字来修饰。这时@property的默认属性是:retainatomic

@synthesize时实现的setter & getter函数等价于下面两个函数:

-(void)setEngine:(Engine *)_engine

{

    if (engine != _engine)

    {

        [engine release];//在赋新值之前,要把旧值release一次。

        engine = [_engine retain];

    } 

}


-(Engine *)engine

 {

    return [[engine retain]autorelease];

 }


而,当我们用@property(nonatomic,retain)来修饰一个是对象指针的成员变量时,

@synthesize时,自动实现的setter & getter函数等价于下面两个函数:

-(void)setEngine:(Engine *)_engine

{

    if (engine != _engine)

    {

        [engine release];//在赋新值之前,要把旧值release一次。

        engine = [_engine retain];

    } 

}


-(Engine *)engine

 {

    return engine ;

 }


注意:两者getter函数的不同。后者getter函数没有retain,那么当使用它对另外一个对象指针赋值时,就要调用retain来计数,且结束后,记得要释放。

例如:对后者的调用方法如下:

    Engine * engine2 = [car.engine  retain] ;

     tail2 release];


********************************************************************************************






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值