delegate举例子实际说明 IOS中的代理

1.1.3 一般意义上的代理
目前为止,我们可以这样来理解代理,A类想要做一件事,但是A类不会做,或不直接做,而是委托B类去做。A委托B,B是A的代理。
1.1.3.1 第一个代理例子
我们定义一个QFDog类,并给这个QFDog增加一个bark方法(叫,咬)
@interface QFDog : NSObject
- (void)bark;
@end
实现这个类的方法
@implementation QFDog
- (void)bark
{
    NSLog(@"Wang wang wang...");
}
- (void)dealloc
{
    NSLog(@"dog dealloc");
    [super dealloc];
}
@end
我们定义一个QFPerson类,这个QFPerson类持有一条dog,并增加一个go的方法
@interface QFPerson : NSObject {
    QFDog *_dog;
}
@property (nonatomic, retain) QFDog *dog;
- (void)go;
@end
@implementation QFPerson
go方法要做的事情就是让person持有的dog对象叫一下,因为person拥有一个指向dog对象的指针,所在在QFPerson里,调用dog的bark方法是没有任何问题的。
- (void)go
{
    [self.dog bark];
}
- (void)dealloc
{
    NSLog(@"person dealloc");
    self.dog = nil;
    [super dealloc];
}
@end
在main函数里,我们键入如下的代码:
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        QFDog *xiaoBai = [[QFDog alloc] init];
        QFPerson *xiaoXin = [[QFPerson alloc] init];
        xiaoXin.dog = xiaoBai;
        [xiaoBai release];
[xiaoXin go];
        [xiaoXin release];
    }
    return 0;
}
好了,我们第一个代理例子写完了。小新听到外面有动静,开始担惊受怕了,但是他年龄太小,自己不敢出去,于是委托小白出去叫一下,帮他震慑一下外面。


1.1.3.2 第二个代理例子
小新的危机解决了,恐怖分子,在小白的震慑下离开了。然后dear tim(第二天),又到了晚上,外面又有动静了,又开始担惊受怕了,又因为年龄太小,又不敢出去,可是,小白走亲戚去了。小新现在做的是委托另外一个,不论是谁,只要能震慑恐怖分子就好了。

所以,这个时候,我们持有的对象,应该是一个id类型,泛型的对象的指针。
我们增加定义一个QFCat的类,并给这个QFCat也增加一个bark方法
@interface QFCat : NSObject
- (void)bark;
@end
实现这个类的方法
@implementation QFCat 
- (void)bark
{
    NSLog(@"Miao miao miao...");
}
- (void)dealloc
{
    NSLog(@"cat dealloc");
    [super dealloc];
}
@end
我们继续写小新的例子,将QFPerson类改为如下这个样子
@interface QFPerson : NSObject {
如果写了property这句可以不写
    id delegate;
}
@property (nonatomic, retain) id delegate;
- (void)go;
@end
@implementation QFPerson
go方法要做的事情就是让person持有的对象叫一下,不管是谁!
- (void)go
{
    [self.delegate bark];
}
- (void)dealloc
{
    NSLog(@"person dealloc");
    self.delegate = nil;
    [super dealloc];
}
@end
然后,我们将main函数的代码改成如下这样:
QFDog *xiaoBai = [[QFDog alloc] init];
        QFPerson *xiaoXin = [[QFPerson alloc] init];
        xiaoXin.delegate = xiaoBai;
        [xiaoBai release];
        [xiaoXin go];
        QFCat *xiaoTom = [[QFCat alloc] init];
        xiaoXin.delegate = xiaoTom;
        [xiaoTom release];
[xiaoXin go];
        [xiaoXin release];
我们编译,运行一下,结果没问题,当小白不在的时候,小汤姆站了出来。我们可以增加小老虎,小青蛙的类,只要都有bark方法,就可以帮我们的小新震慑恐怖分子了。


1.1.3.3 第三个代理例子
但是,这样是不规范的,我们没有规范具体的行为,一旦小新的delegate是一个没有实现bark方法的对象的时候,我们的程序就会崩溃了。正常的做法是,我们可以通过协议来规范接口。
增加一个ProtectDelegate协议,并声明一个必须实现的- (void)bark;方法。
@protocol ProtectDelegate <NSObject>
- (void)bark;
@end
QFCat类QFDog类头文件,遵守ProtectDelegete协议,并删除bark方法声明,如下:
#import "ProtectDelegate.h"
@interface QFCat : NSObject <ProtectDelegate>
@end
#import "ProtectDelegate.h"
@interface QFDog : NSObject <ProtectDelegate>
@end
QFPerson类,改为如下这样:
#import "ProtectDelegate.h"
@interface QFPerson : NSObject {
如果写了property这句可以不写
这个类型也是泛型,但是是遵守ProtectDelegate协议的泛型的指针
    id <ProtectDelegate> _delegate;
}
_delegate的setter和getter方法,也要设置遵守ProtectDelegate协议的对象的指针
@property (nonatomic, retain) id <ProtectDelegate> delegate;
- (void)go;
@end
运行一下,结果也是没有问题。
我们通过协议,规范接口,只有遵守ProtectDelegate协议的对象,才能够被设置为小新的delegate。自然,遵守ProtectDelegate协议的对象,自然实现了bark方法。
1.1.3.4 第四个代理例子 (正向传值)
恐怖分子被一次又一次的震慑回去了,时间久了,仅仅凭借大叫一声,已经很难震撼到他们了,所以,我们要做的是多叫几次,这就用到了我们的正向传值。
修改协议中得bark方法,增加一个整型的参数count
@protocol ProtectDelegate <NSObject>
- (void)bark:(NSInteger)count;
@end
QFCat和QFDog中的方法也跟着改为如下形式
- (void)bark:(NSInteger)count
{
    for (int i=1; i<=count; i++) {
        NSLog(@"Miao miao miao...%d",i);
    }
}
- (void)bark:(NSInteger)count
{
    for (int i=1; i<=count; i++) {
        NSLog(@"Wang wang wang...%d",i);
    }
}
QFPerson类的声明增加count的property,_count成员变量,setter和getter方法就都有了。
@interface QFPerson : NSObject {
    id  <ProtectDelegate> _delegate;
}
@property (nonatomic, retain) id <ProtectDelegate> delegate;
@property (nonatomic, assign) NSInteger count;
- (void)go;
@end
实现里go方法也要响应的修改
- (void)go
{
    [_delegate bark:_count];
}
在main函数里增加count的次数
int main(int argc, const char * argv[])
{
    @autoreleasepool {
        QFDog *xiaoBai = [[QFDog alloc] init];
        QFPerson *xiaoXin = [[QFPerson alloc] init];
        xiaoXin.count = 5;
        xiaoXin.delegate = xiaoBai;
        [xiaoBai release];
        [xiaoXin go];
        QFCat *xiaoTom = [[QFCat alloc] init];
        xiaoXin.delegate = xiaoTom;
        [xiaoXin go];
        [xiaoTom release];
        [xiaoXin release];
    }
    return 0;
}
执行程序,无论是小汤姆还是小白,都叫了5次,将xiaoXin中的count的值传递给了小白和小汤姆,正向传值完成。其实,无论带不带参数,小新类里调用小白,小汤姆的方法,这就可以成为传值。


1.1.3.5 第五个代理例子 (反向传值)
面对着恐怖分子的一次次骚扰,小新实在是忍无可忍,开始反击了。于是,他决定升级他的守护犬小白,自然少不了炼化,羽化,幻化。又给小白打造了一身装备,各种锻造,各种红橙黄绿青蓝紫的宝石一起用上,加攻击,加防御,并带有减速、冰冻、灼热、中毒效果的神小白诞生了。神小白除了能大叫震慑恐怖分子之外,更可以对恐怖分子造成杀伤,所以,神小白做一件事情,将它杀死的恐怖分子的个数,回传给小新,这就用到了反向传值。
之所以小新能把值传给小白,小汤姆,是因为小新类拥有了小白或小汤姆的指针。所以,想要回传,神小白类里,就应该有小新的指针。
我们在QFPerson的定义里增加killed方法的声明,用于给小白调用,反向传值,告诉小新,小白杀死了多少恐怖分子
- (void)killed:(NSInteger)count;
在QFPerson的实现里,增加killed:方法的实现
- (void)killed:(NSInteger)count
{
    NSLog(@"杀死了%ld个恐怖分子!",count);
}
我们将QFDog类增加master的property,让神小白可以保存小新的指针
注意,这个地方可以先写成#improt "QFPerson.h" 但代码写完后,编译报错,此时先跳过这个问题,告诉学生先这样写,一会来解释这个问题。
@class QFPerson;
@interface QFDog : NSObject <ProtectDelegate>
@property (nonatomic, retain) QFPerson *master;
@end
这个QFDog实现里要增加这行代码:#import "QFPerson.h"
#import "QFPerson.h"
@implementation QFDog
- (void)bark:(NSInteger)count
{
    for (int i=1; i<=count; i++) {
        NSLog(@"Wang wang wang...%d",i);
    }
    [_master killed:arc4random()%20+1];
}


- (void)dealloc
{
    NSLog(@"dog dealloc");
    [super dealloc];
}
@end
我们再将main函数里,增加神小白的主人的设置。在xiaoXin.delegate = xiaoBai;后面增加如下一行代码:
        xiaoBai.master = xiaoXin;
执行一下,小新知道小白咬死多少恐怖分子了,神小白将自己对象里的值,传给了小新,反向传值完成。
反向传值解决了,但我们在QFDog声明里用了@class,而且,看打印结果,小新对象和小汤姆对象没有释放,内存泄露,下面来解决循环包含和循环引用。


  1.1.4 循环包含与循环引用
循环包含,A类的头文件里导入B类的头文件,同时B类头文件里又导入A类头文件,这样,在展开的时候,就会出现无限展开,根本停不下来的状况。OC的#import可以解决头文件重复包含的问题,但解决不了头文件循环包含的问题。
用@class来解决这个问题,@class做的事情是一个类的预声明,@class QFPerson; 只表明QFPerson是一个类。
在QFDog.h里面用@class后,需要在QFDog.m里重新导入#import "QFPerson.h" ,因为@class只是表明QFPerson是一个类,只有真正导入头文件,才能使用QFPerson类里的方法。


循环引用,A类的对象持有B类的对象,B类的对象又持有A类的对象,造成相互引用,相互持有,这样,谁的引用计数都不会变为0,谁都不会释放。就像两个铁环,相互咬在一起,就无法拽开了。
解决的办法就是砍断一个铁环。将一个retain改为assign。




  1.1.5 OC或IOS里真正使用的代理
在OC特别是IOS开发里面,代理的用法和上面恰恰相反,上面的例子里,我们拿小新作为主视角,但是在UI里的代理使用是拿小白或小汤姆作为主视角。
小新,可以类比为UI里的”界面”,小白或小汤姆,可以类比为界面的一个个控件。(这个等我们到UI里就能看得见摸得着,就能更好的理解这句话了)
1.1.5.1 第六个代理例子
我们把主视角从小新身上移开,转移到神小白身上。神小白在无数次的跟恐怖分子的战斗后,终于升到了满级。正所谓能力越大,责任也越大,这时候的神小白,就不只为一个人战斗了,为全民族,为全人类,全宇宙。战斗依然继续,转瞬间,物是人非,很多东西都在改变,可唯一不会改变的是,神小白那个习惯,杀死敌人后,跟他的长官汇报一下他的战斗成果。但是,这个长官未必是小新,也可能是如来,界王神,宇宙大帝等等,所以,就不能是QFPerson类型的指针了,而是要改为id类型,而且,还不能是单纯的id类型,应该是可以像小新那样,可以听的懂神小白汇报的人。
(新Demo)所以,我们先制定一个可以听懂小白汇报的协议
@protocol ReceiveReportDelegate <NSObject>
- (void)killed:(NSInteger)count;
@end
接下来定义一个如来的类,要遵守ReceiveReportDelegate协议,以及实现
#import "ReceiveReportDelegate.h"
@interface QFRuler : NSObject <ReceiveReportDelegate>
@end
@implementation QFRuler
- (void)killed:(NSInteger)count
{
    NSLog(@"Good job killed %ld devil",count);
}


- (void)dealloc
{
    NSLog(@"ruler dealloc");
    [super dealloc];
}
@end
我们定义神小白的类,以及实现
#import "ReceiveReportDelegate.h"
@interface QFGodDog : NSObject
这里我们用assign而不用retain,防止循环引用
@property (nonatomic, assign) id <ReceiveReportDelegate> delegate;
- (void)bark;
@end
@implementation QFGodDog
- (void)bark
{
    [_delegate killed:arc4random()%20+1];
}
- (void)dealloc
{
    NSLog(@"god dog dealloc");
    [super dealloc];
}
@end
在main函数里,我们就可以写如下的代码了
        QFRuler *ruler = [[QFRuler alloc] init];
        QFGodDog *godXiaoBai = [[QFGodDog alloc] init];
        godXiaoBai.delegate = ruler;
        [godXiaoBai bark];
        [ruler release];
        [godXiaoBai release];
执行程序。
我们还可以在增加一个界王神的类,也要遵守ReceiveReportDelegate协议
#import "ReceiveReportDelegate.h"
@interface QFJean : NSObject <ReceiveReportDelegate>
@end
@implementation QFJean
- (void)killed:(NSInteger)count
{
    NSLog(@"Yeah! killed %ld Buu",count);
}


- (void)dealloc
{
    NSLog(@"ruler dealloc");
    [super dealloc];
}
@end
我们可以将main函数写成如下形式
        QFJean *jean = [[QFJean alloc] init];
        QFGodDog *godXiaoBai = [[QFGodDog alloc] init];
        godXiaoBai.delegate = jean;
        [godXiaoBai bark];
        [jean release];
        [godXiaoBai release];
这样,神小白的汇报对象,就改为界王了,程序也没有问题,只要他们都遵守ReceiveReportDelegate协议,都能听的懂神小白的汇报就可以了。
神小白,就好比我们UI里的控件,一个类型的控件可以在多个不同的界面里使用,可以通过代理这种方式,进行传值。更重要的时,通过代理这种设计思想,降低了类与类之间的耦合,可以实现通用类,来增加代码的复用,节省开发时间,降低开发成本,提高开发效率。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值