回调方式总结

2018-04-04  本文已影响4人  54番茄

今天对ios开发中常用的几种回调方式:block、delegate与NSNotification进行一下总结,并对在实际开发中该如何选择。在我们日常的开发任务中,我们经常要用到一些回调的手段,譬如说网络请求操作。

先说KVO吧,它是cocoa框架实现的观察者模式,一般同KVC搭配使用。通过KVO可以监测一个值的变化,比如View的frame变化,一个Person模型的属性字变化等。是一对多的关系,一个值的变化会通知所有的观察者。KVO一般的使用场景是数据,需求是数据变化,比如实时商品价格变动,一般使用KVO(观察者模式)。

//新建一个person类
@interface Person : NSObject
@property (nonatomic,copy) NSString * name;
@end

//ViewController中new一个Person然后它监听
@interface ViewController ()
@property(nonatomic,strong) Person * p ;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    _p= [[Person alloc]init];
    _p.name = @"番茄";
    [_p addObserver:self forKeyPath:@"name" options:  NSKeyValueObservingOptionNew |
     NSKeyValueObservingOptionOld  context:nil];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
  NSLog(@"输出了旧值与新值");  
}

-(void)changePersonAction{
    //改变了,值会出发上面监听方法
     _p.name = @"bu ai le";
}

NSNotification是通知,也是一对多的使用场景。在某些情况下,KVO和NSNotification是相似的,都是状态变化之后告知对方。NSNotification的特点,就是需要被观察者先主动发出通知,然后观察者注册监听后再来进行响应,比KVO多了发送通知的一步,但是其优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,使用也更灵活。

//创建一个监听
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notificationTest) name:@"notificationTest" object:nil];

-(void)notificationTest{
    NSLog(@"收到通知了");
}
//主动打出通知
[[NSNotificationCenter defaultCenter] postNotificationName:@"notificationTest" object:nil];

delegate 是代理,其存在的意义就是我不想做的事情交给别人做,我告诉你我要做什么,但是具体怎么做,那是你的事了。比如 A类——>B类,B不想做的事情,就交给A类处理,A类要遵循B的delegate就可以了,其他就由A类完成所需要的操作。所以delegate是一对一关系。

BViewController类

//定义代理
@protocol BDelegate <NSObject>
-(void)getACCName:(UIColor*)clolro;
-(void)testAction1;
-(void)testAction2;
@end
//声明代理
@interface BViewController : UIViewController
@property (nonatomic,weak) id<ADelegate> delegate;
@end

//respondsToSelector 判断方法是否实现了,实现了就去调用,防止出现异常,造成crash
UIColor * redC = [UIColor redColor];
if(_delegate &&  [_delegate respondsToSelector:@selector(getACCName:)]){
        [self.delegate getACCName:redC];
 }
 if(_delegate &&  [_delegate respondsToSelector:@selector(testAction1)]){
        [self.delegate testAction1];
 }
 if(_delegate &&  [_delegate respondsToSelector:@selector(testAction2)]){
        [self.delegate testAction2];
 }


############AViewController类###############
UIStoryboard * sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
BViewController * av = [sb instantiateViewControllerWithIdentifier:@"BViewController"];
av.delegate = self;
[self.navigationController pushViewController:av animated:YES];

//实现代理事件
-(void)testAction1{
    NSLog(@"触发了testAction1");
}
-(void)testAction2{
    NSLog(@"触发了testAction2");
}

block是delegate的另一种形式,是函数式编程的一种形式。使用场景跟delegate一样,相比delegate更灵活,而且代理的实现更直观,可以追溯事件过程。如果没有block,有可能出现一个类需要使用N多其他类时,要遵循N多个delegate,实现N多个delegate方法的情况,逻辑到处散落,难以理清。但也不是说block就可以完全替代delegate,,要看情况结合使用才是王道。

//举例把 block 声明成属性
typedef void(^MyBlcok)(int a);
@interface BViewController ()
@property(nonatomic,copy) MyBlcok myBlock;

@property (nonatomic,copy) void(^youBlock)(void);//等同于上面两个的结合
@end

############在BViewController.h中声明成属性###############
@interface BViewController : UIViewController
@property (nonatomic,copy) void(^youBlock)(void);
@end
//这种就完成类似delgate的功能
UIStoryboard * sb= [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    BViewController * av = [sb instantiateViewControllerWithIdentifier:@"BViewController"];
    av.delegate = self;
    av.youBlock = ^{
        //事件响应
    };
    [self.navigationController pushViewController:av animated:YES];

//block作为参数
//封装网络请求
-(NSURLSessionTask*)GET:(NSString*)url parameter:(id)parameter singleResultCompletionBlock:(void(^)(NSDictionary * result,NSError * error))block;

KVO
优点

缺点:

Delegate
优点:

缺点:

适用场景:UI响应事件,回调方法(网络请求)

Block
优点:

缺点:

注:使用 block 时稍微不注意就形成循环引用,导致对象释放不了。这种循环引用,一旦出现就比较难检查出来。而 delegate 的方法是分离开的,并不会引用上下文,因此会更安全些。解决Block循环引用问题:当在大括号中出现self或者下划线的属性,可能造成死循环__weak typeof (self) weakSelf=selftypeof( )会自动识别括号中的对象类型

block出栈需要将使用的数据从栈内存拷贝到堆内存,当然对象的话就是加计数,使用完或者block置nil后才消除。delegate只是保存了一个对象指针,直接回调,没有额外消耗。就像C的函数指针,只多做了一个查表动作。

在事件处理方面:代理更加注重事件的过程:如发起一个网络请求,可能想要知道此时请求是否已经开始、是否收到了数据、数据是否已经接受完成、数据接收失败,常见使用UIWebview的代理事件(开始加载,加载中,加载完成.........)。而Block更注重事件处理的结果:比如对于一个事件,只想知道成功或者失败,并不需要知道进行了多少或者额外的一些信息。

适用场景:网络请求

Notification:
优点:

缺点:

适用场景:跨层通信,需要实现在两个毫无关联的对象之间的通信。

注:我们都知道,通知和定时器,我们需要手动释放,否则会造成内存泄漏,我们看到网上有说通知的释放可以在dealloc方法中进行释放,这个是没有错的,但是在实际的开发中,我们在不同的实际需求,释放的方法是不一样的。例如:现在A界面是在不断的接受通知。此时A跳到B界面,而在B界面中不需要A通知的事件,如果在A界面中将通知的释放写在dealloc中,跳到B界面,不会执行A界面中的dealloc方法。所以A界面仍然不断的接收着消息。所以这种需求,就应该讲A中的通知释放写在viewDidDisapper方法中。

总结:使用场景对比

1.回调方法:在日常的开发过程中,我们经常会遇到一些完成之后的处理问题,比如完成网路请求之后的回调,或者页面加载完成之后的回调等。这个时候我们一般使用的是前两者方法,即Block或者Delegate。而在一对一传输回 调的时候明显Block的使用更加的简单高效,只需要在代码块中执行所需要的操作即可。在一对多的情况下,Delegate更加能够发挥出自己的优势。

2.跨层通信 :有的时候我们需要实现在两个毫无关联的对象之间的通信,这个时候如果使用Block或者Delegate就势必会增加代码的耦合性,这样对于代码的结构来说是不健康的,因此这个时候使用Notification便是明智的选择。

3.UI响应事件 :用户在于App的UI进行互动的时候,总会需要App进行交互响应,这个时候就毫无疑问的使用代理设计模式。而苹果官方给出的建议也是可以肯定的,在Cocoa Touch框架中我们也可以在几乎所有的UI交互控件的头文件里看到Delegate的成员变量,也正是印证了在UI响应事件上Delegate有着绝对的优势。

4.简单值得传递 :当需要进行简单值得传递的时候,比如子控件传输给父控件所点击的IndexPath的时候,更加适合使用Block来传值。因为,如果只是为了传这一个简单的值而没有特别的业务处理而定义一个协议,然后实现协议,设置代理再写方法的话将十分麻烦,得不偿失,这个时候简单高效的Block就可以完美的替代Delegate完成任务了。

上一篇下一篇

猜你喜欢

热点阅读