我们在开发中常常会用到函数回调,你可以用通知来替代回调,但是大多数时候回调是比通知方便的,所以何乐而不为呢?如果你不知道回调使用的场景,我们来假设一下:
1.我现在玩手机
2.突然手机没有电了
3.我只好让手机开始充电
4.充电的过程中我好无聊,我要去看电视,但是我不会一直看电视,我要等手机电充满了停止看电视,继续去玩手机
5.我开始看电视
6.手机电充好了,我听到手机响了一下,我不看电视了我继续去玩手机。
这个场景中哪里跟回调类似呢?哪里跟通知类似呢?其实我们可以认为手机充好电了通过回调的方式让我继续玩手机,也可以认为手机充好电了通知我可以继续玩手机,然后我主动继续玩手机。这里更像通知不像回调。但是换个思维想,如果手机本身没有回调机制,那他怎么能在恰好手机刚充满的时候响一下呢?
先不纠结这个问题,我们看看如果用block怎样来实现这样一个场景:
我们随便找个控制器写下以下代码:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"我在玩手机");
NSLog(@"手机没电了");
[self chargeMyIphone];
NSLog(@"我在看电视");
}
-(void)chargeMyIphone
{
[NSThread sleepForTimeInterval:10];
NSLog(@"电充好了");
}
注意 这里我用了NSTread sleep,这样会让我的主线程沉睡10秒钟,这个过程中我我真的可以一边看电视一边充电吗?
所以我们应该让充电的线程和我看电视的线程错开执行!这里我们就不开新线程了,就让他10秒之后再执行吧。模拟下:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"我在玩手机");
NSLog(@"手机没电了");
[self performSelector:@selector(chargeMyIphone:) withObject:Nil afterDelay:10];
NSLog(@"我在看电视");
}
改一下这一句代码后,我们再看看:
2013-09-17 00:47:54.786
故事版应用[1013:a0b]我在玩手机
2013-09-17 00:47:54.787
故事版应用[1013:a0b]手机没电了
2013-09-17 00:47:54.787
故事版应用[1013:a0b]我在看电视
2013-09-17 00:48:04.799
故事版应用[1013:a0b]电充好了
看起来没多大问题,但是我们还没写完我们的场景呢,我们想充好电之后继续玩手机?所以我们写在哪儿呢?
如果直接放在看电视后面:
NSLog(@"继续玩手机");
我们看看控制台:
2013-09-17 00:50:12.417
故事版应用[1029:a0b]我在玩手机
2013-09-17 00:50:12.418
故事版应用[1029:a0b]手机没电了
2013-09-17 00:50:12.419
故事版应用[1029:a0b]我在看电视
2013-09-17 00:50:12.419
故事版应用[1029:a0b]继续玩手机
2013-09-17 00:50:22.431
故事版应用[1029:a0b]电充好了
呵呵!电都没充好,你就直接继续玩了?所以这里应该是电充好以后 我们再继续玩手机?
那么该怎么做?我们可以写进充电函数里吗?
-(void)chargeMyIphone
{
NSLog(@"电充好了");
NSLog(@"继续玩手机");
}
我们看看控制台:
2013-09-17 00:51:43.832
故事版应用[1044:a0b]我在玩手机
2013-09-17 00:51:43.833
故事版应用[1044:a0b]手机没电了
2013-09-17 00:51:43.833
故事版应用[1044:a0b]我在看电视
2013-09-17 00:51:53.848
故事版应用[1044:a0b]电充好了
2013-09-17 00:51:53.849
故事版应用[1044:a0b]继续玩手机
看起来没多大问题啊!但是我们想想看,我们把继续玩手机这件事情写在了充电函数的最后?
如果说我每次冲完电都继续玩手机,这个没什么问题!但是如果我每次冲完电之后不总是玩手机怎么办呢?
比如有一次我充完电之后想出门逛街!这个很好理解吧,那么这样写就不对了!我们想让充电函数最后执行的那一行是可以变化的。有很多方式可以做到,但是这里最好的做法肯定是追加一个block替换掉我们写死的那句代码啦!
也就是说我在充电前已经安排好一个充电后的计划了,今天冲完电继续玩手机,明天冲完电出门逛街,那么我每次调用的还是那个充电函数,只是传的参数不一样而已!我们不用传int 1表示玩手机,2表示出门逛街,我们直接把这两件事情当做参数传过去!
明白这一点就知道block的最终奥义啦!所以我们该改造一下这个函数,为他加个包含代码的参数!
-(void)chargeMyIphone:(void(^)(void))finishBlock
{
NSLog(@"电充好了");
//NSLog(@"继续玩手机");
finishBlock();
}
这个追加的参数就是我们的block了,第一个void表示此block无返回值。(^)为block type的标志。第二个(void)表示这个block无参数。finishBlock就是他的名字。无参数无返回类型的匿名函数就是我们的最简单的block了!他非常方便我们用来回调,因为他没有返回值,没有参数,就相当于只有内部的可执行代码!
而我们将一个固定的事件用一段代码作为参数传了进来,并且以 名字() 形式来触发它,那么这个函数的结尾就不会总是玩手机了!那么他可以是任何事情!
所以我们来尝试调用下这个函数:
但是这里的参数我们该怎么传呢?我们通过这种方式传block参数好像不符合他这的object,所以我们还是直接调吧,但是把这一行用dispatch_after包起来~
按一下回车:
然后在中间写我要做什么,这里是出门逛街;
接下来在chargeIphone内部敲dis,然后联想出来选择第一个按回车:
然后将时间改为10,把整个方法内的代码移动进去:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"我在玩手机");
NSLog(@"手机没电了");
[self chargeMyIphone:^{
NSLog(@"出去逛街");
}];
NSLog(@"我在看电视");
}
-(void)chargeMyIphone:(void(^)(void))finishBlock
{
double delayInSeconds = 10.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(@"电充好了");
finishBlock();
});
}
现在代码的结构非常清晰:
充电完成之后 我要去逛街。
充电内部需要耗时10秒。
充电的同时我可以看电视。
充电完成之后回头来触发我block中设置的出去逛街。
这样设置block的好处我已经说过了,我们没有把充电函数内部的实现写死,也就是说当我完成之后无论做什么都无所谓,调的地方不同,传不同的代码过去就可以了,这个跟函数指针类似吧。
我们来运行一下程序验证下最后的结果:
2013-09-17 01:17:23.127
故事版应用[1088:a0b]我在玩手机
2013-09-17 01:17:23.129
故事版应用[1088:a0b]手机没电了
2013-09-17 01:17:23.129
故事版应用[1088:a0b]我在看电视
2013-09-17 01:17:33.130
故事版应用[1088:a0b]电充好了
2013-09-17 01:17:33.131
故事版应用[1088:a0b]出去逛街
完全验证了我们的结论,23秒开始充电 ,看电视,33秒的适合充好电出去逛街。
OK ,最简单的block我们就讲到这里,用好他来给你的代码非写死吧!DON'T HARD WRITE
分享到:
相关推荐
5.3.7 预留回调接口 5.3.8 编程中的DRY规则 5.3.9 用hash对象传参 5.4 面向对象编程 5.4.1 面向过程编程和面向对象编程 5.4.2 JavaScript的面向对象编程 5.4.3 用面向对象方式重写代码 5.5 其他问题 5.5.1 ...
Asp.Net2.0无刷新客户端回调 体验.net 2.0 的优雅(1) -- 异步WebService调用 ASP.NET 2.0页面框架的几点新功能 ASP.NET 2.0 中收集的小功能点 asp.net2.0中的webpart使用小记 2.0问题、错误解决办法 ASP.NET 2.0...
营里(戴维营)的兄弟都对Objective-C很熟悉,许多人多block情有独钟,将各种回调函数、代理通通都用它来实现。甚至有人选择用FBKVOController、BlocksKit等开源框架将KVO、控件事件处理都改为通过block解决。原因...
3.12 块 block ......................................................................... 2 3.13 PSAM ............................................................................ 3 3.14 TYPE A ............
可以在方法中更改该值,但当控制传递回调用过程时,不会保留更改的值。通过使用方法参数关键字,可以更改这种行为。如果没有ref,out则默认为值传递,虽然可以在方法中修改这个参数的值,但是修改后的值不会还会到调用...
例如,如果您设计一个具有可调整输入数量的封装子系统,当用户更改所需的输入数量时,初始化回调将需要创建额外的或销毁额外的模块输入端口,并将它们连接到子系统中的所需目的地. 设计原则 这些函数通常接受模块...
我们先进行一个简单的纯SIP信令(不带语音连接建立)的UAC的SIP终端的程序开发的试验(即一个只能作为主叫不能作为被叫的的SIP软电话模型),我们创建一个MFC应用程序,对话框模式,照上面的说明,...
11.1.3使用回调函数作为异步访问的通知 11.1.4异步访问与设备驱动 11.2异步Fifo驱动例子 11.2.1在virtualfifo驱动中增加异步通知 11.2.2在用户空间验证virtualfifo的异步通知 第12章Linux块设备驱动 12.1块设备的I/O...
它是一个用户定义的回调函数,在每次从调试堆分配内存之前被调用。在初始化时,Visual Leak Detector使用_CrtSetAllocHook注册这个钩子函数,这样就可以监视从此之后所有的堆内存分配了。 如何保证在Visual Leak ...
1.4.7 HelloWDM对其他IRP的回调函数 1.4.8 HelloWDM的卸载例程 1.5 HelloWDM的编译和安装 1.5.1 用DDK编译环境编译HelloWDM 1.5.2 HelloWDM的编译过程 1.5.3 安装HelloWDM 1.6 小结 第2章 ...
1.4.7 HelloWDM对其他IRP的回调函数 1.4.8 HelloWDM的卸载例程 1.5 HelloWDM的编译和安装 1.5.1 用DDK编译环境编译HelloWDM 1.5.2 HelloWDM的编译过程 1.5.3 安装HelloWDM 1.6 小结 第2章 ...
1.1 什么是局部程序块(local block)? 8 1.2 可以把变量保存在局部程序块中吗? 9 1.3 什么时候用一条switch语句比用多条if语句更好? 9 1.4 switch语句必须包含default分支吗? 10 1.5 switch语句的最后一个分支...