IOS开发之---初识Block

正文

Block简介

我们可以把Block当做Objective-C的匿名函数。Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观。另外,block的实现具有封闭性(closure),而又能够很容易获取上下文的相关状态信息。

Block的创建

实际上,block使用了与函数相同的机制:可以像声明函数一样,来声明一个bock变量;可以利用定义一个函数的方法来定义一个block;也可以将block当做一个函数来调用。

 1     // main.m
 2     #import <Foundation/Foundation.h>
 3
 4     int main(int argc, const char * argv[]) {
 5         @autoreleasepool {
 6             // Declare the block variable
 7             double (^distanceFromRateAndTime)(double rate, double time);
 8
 9             // Create and assign the block
10             distanceFromRateAndTime = ^double(double rate, double time) {
11                 return rate * time;
12             };
13             // Call the block
14             double dx = distanceFromRateAndTime(35, 1.5);
15
16             NSLog(@"A car driving 35 mph will travel "
17                   @"%.2f miles in 1.5 hours.", dx);
18         }
19         return 0;
20     } 

在上面的代码中,利用插入符(^)将distanceFromRateAndTime变量标记为一个block。就像声明函数一样,需要包含 返回值的类型,以及参数的类型,这样编译器才能安全的进行强制类型转换。插入符(^)跟指针(例如 int *aPointer)前面的星号(*)类似——只是在声明的时候需要使用,之后用法跟普通的变量一样。

block的定义本质上跟函数一样——只不过不需要函数名。block以签名字符串开始:^double(double rate, double time)标示返回一个double,以及接收两个同样为double的参数(如果不需要返回值,可以忽略掉)。在签名后面是一个大括弧({}),在这个 括弧里面可以编写任意的语句代码,这跟普通的函数一样。

当把block赋值给distanceFromRateAndTime后,我们就可以像调用函数一样调用这个变量了。

不带参数的Block

如果block不需要任何的参数,那么可以忽略掉参数列表。另外,在定义block的时候,返回值的类型也是可选的,所以这样情况下,block可以简写为^ { … }:

1     double (^randomPercent)(void) = ^ {
2         return (double)arc4random() / 4294967295;
3     };
4     NSLog(@"Gas tank is %.1f%% full",
5           randomPercent() * 100); 

在上面的代码中,利用内置的arc4random()方法返回一个32位的整型随机数——为了获得0-1之间的一个值,通过除以arc4random()方法能够获取到的最大值(4294967295)。

到现在为止,block看起来可能有点像利用一种复杂的方式来定义一个方法。事实上,block是被设计为闭包的(closure)——这就提供了一种新的、令人兴奋的编程方式。

Block的闭包性(closure)

在block内部,可以像普通函数一样访问数据:局部变量、传递给block的参数,全局变量/函数。并且由于block具有闭包性,所以还能 访问非局部变量(non-local variable)。非局部变量定义在block之外,但是在block内部有它的作用域。例如,getFullCarName可以使用定义在block 前面的make变量:

1     NSString *make = @"Honda";
2     NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
3         return [make stringByAppendingFormat:@" %@", model];
4     };
5     NSLog(@"%@", getFullCarName(@"Accord"));    // Honda Accord 

非局部变量会以const变量被拷贝并存储到block中,也就是说block对其是只读的。如果尝试在block内部给make变量赋值,会抛出编译器错误。

以const拷贝的方式访问非局部变量,意味着block实际上并不是真正的访问了非局部变量——只不过在block中创建了非局部变量的一个 快照。当定义block时,无论非局部变量的值是什么,都将被冻结,并且block会一直使用这个值,即使在之后的代码中修改了非局部变量的值。下面通过 代码来看看,在创建好block之后,修改make变量的值,会发生什么:

1     NSString *make = @"Honda";
2     NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
3         return [make stringByAppendingFormat:@" %@", model];
4     };
5     NSLog(@"%@", getFullCarName(@"Accord"));    // Honda Accord
6
7     // Try changing the non-local variable (it won‘t change the block)
8     make = @"Porsche";
9     NSLog(@"%@", getFullCarName(@"911 Turbo")); // Honda 911 Turbo 

block的闭包性为block与上下文交互的时候带来极大的便利性,当block需要额外的数据时,可以避免使用参数——只需要简单的使用非局部变量即可。

修改非局部变量

冻结中的非局部变量是一个常量值,这也是一种默认的安全行为——因为这可以防止在block中的代码对非局部变量做了意外的修改。那么如果我们 希望在block中对非局部变量值进行修改要如何做呢——用__block存储修饰符(storage modifier)来声明非局部变量:

1     __block NSString *make = @"Honda"; 

这将告诉block对非局部变量做引用处理,在block外部make变量和内部的make变量创建一个直接的链接(direct link)。现在就可以在block外部修改make,然后反应到block内部,反过来,也是一样。

通过引用的方式访问非局部变量

这跟普通函数中的静态局部变量(static local variable)类似,用__block修饰符声明的变量可以记录着block多次调用的结果。例如下面的代码创建了一个block,在block中对i进行累加。

1 __block int i = 0;
2 int (^count)(void) = ^ {
3     i += 1;
4     return i;
5 };
6 NSLog(@"%d", count());    // 1
7 NSLog(@"%d", count());    // 2
8 NSLog(@"%d", count());    // 3

Block作为函数的参数

把block存储在变量中有时候非常有用,比如将其用作函数的参数。这可以解决类似函数指针能解决的问题,不过我们也可以定义内联的block,这样代码更加易读。

例如下面Car interface中声明了一个方法,该方法用来计算汽车的里程数。这里并没有强制要求调用者给该方法传递一个常量速度,相反可以改方法接收一个block——该block根据具体的时间来定义汽车的速度。

 1     // Car.h
 2     #import <Foundation/Foundation.h>
 3
 4     @interface Car : NSObject
 5
 6     @property double odometer;
 7
 8     - (void)driveForDuration:(double)duration
 9            withVariableSpeed:(double (^)(double time))speedFunction
10                        steps:(int)numSteps;
11
12     @end 

上面代码中block的数据类型是double (^)(double time),也就是说block的调用者需要传递一个double类型的参数,并且该block的返回值为double类型。注意:上面代码中的语法基本 与本文开头介绍的block变量声明相同,只不过没有变量名字。

在函数的实现里面可以通过speedFunction来调用block。下面的示例通过算法计算出汽车行驶的大约距离。其中steps参数是由调用者确定的一个准确值。

 1     // Car.m
 2     #import "Car.h"
 3
 4     @implementation Car
 5
 6     @synthesize odometer = _odometer;
 7
 8     - (void)driveForDuration:(double)duration
 9            withVariableSpeed:(double (^)(double time))speedFunction
10                        steps:(int)numSteps {
11         double dt = duration / numSteps;
12         for (int i=1; i<=numSteps; i++) {
13             _odometer += speedFunction(i*dt) * dt;
14         }
15     }
16
17     @end 

在下面的代码中,有一个main函数,在main函数中block定义在另一个函数的调用过程中。虽然理解其中的语法需要话几秒钟时间,不过这比起另外声明一个函数,再定义withVariableSpeed参数要更加直观。

 1     // main.m
 2     #import <Foundation/Foundation.h>
 3     #import "Car.h"
 4
 5     int main(int argc, const char * argv[]) {
 6         @autoreleasepool {
 7             Car *theCar = [[Car alloc] init];
 8
 9             // Drive for awhile with constant speed of 5.0 m/s
10             [theCar driveForDuration:10.0
11                    withVariableSpeed:^(double time) {
12                                return 5.0;
13                            } steps:100];
14             NSLog(@"The car has now driven %.2f meters", theCar.odometer);
15
16             // Start accelerating at a rate of 1.0 m/s^2
17             [theCar driveForDuration:10.0
18                    withVariableSpeed:^(double time) {
19                                return time + 5.0;
20                            } steps:100];
21             NSLog(@"The car has now driven %.2f meters", theCar.odometer);
22         }
23         return 0;
24     } 

上面利用一个简单的示例演示了block的通用性。在iOS的SDK中有许多API都利用了block的其它一些功能。NSArray的 sortedArrayUsingComparator:方法可以使用一个block对元素进行排序,而UIView的 animateWithDuration:animations:方法使用了一个block来定义动画的最终状态。此外,block在并发编程中具有强大 的作用。

定义Block类型

由于block数据类型的语法会很快把函数的声明搞得难以阅读,所以经常使用typedef对block的签名(signature)做处理。 例如,下面的代码创建了一个叫做SpeedFunction的新类型,这样我们就可以对withVariableSpeed参数使用一个更加有语义的数据 类型。

 1     // Car.h
 2     #import <Foundation/Foundation.h>
 3
 4     // Define a new type for the block
 5     typedef double (^SpeedFunction)(double);
 6
 7     @interface Car : NSObject
 8
 9     @property double odometer;
10
11     - (void)driveForDuration:(double)duration
12            withVariableSpeed:(SpeedFunction)speedFunction
13                        steps:(int)numSteps;
14
15     @end 

许多标准的Objective-C框架也使用了这样的技巧,例如NSComparator。

总结

Block不仅提供了C函数同样的功能,而且block看起来更加直观。block可以定义为内联(inline),这样在函数内部调用的时候 就非常方便,由于block具有闭包性(closure),所以block可以很容易获得上下文信息,而又不会对这些数据产生负面影响。

本文转自:http://www.cocoachina.com/industry/20130710/6569.html

延伸阅读:

深度围观block(中文译文)

时间: 2024-10-05 05:00:21

IOS开发之---初识Block的相关文章

IOS开发- 用block实现回调

在IOS开发中经常会用到回调的情况,下面介绍如何用block实现回调. 1 #import <Foundation/Foundation.h> 2 3 @interface BLock : NSObject 4 5 + (void)getBlock:(void (^)(NSString *))someblock; 6 7 @end BLock.h 1 #import "BLock.h" 2 #import <Foundation/Foundation.h> 3

ios开发——实用技术篇&amp;Block/KVO/通知/代理

Block/KVO/通知/代理简单介绍与使用 关于iOS开发中数据传递的方法有很多种,但是使用最多的就是这里的四种,而且我们要学会在适当的时候使用合适的方式,才能充分的提高app的性能 下面简单介绍一下这些方法的使用 Block 第一.综述 block是OC中另外一种对象和对象的通信方式,是一对一的关系,类似于delegate,而通知时一对多的关系 第二.定义block类型 int (^myBlock)(int) 第三.block的声明 mylock=^(int a) { int result

iOS开发Delegate,Notification,Block使用心得

(一)简要介绍 1.Delegate(代理.委托) 代理几乎是iOS开发中最常用的传值方式,在项目中的AppDelegate就是使用的这种设计模式,不仅如此,还有很多原生的控件也使用的这种设计模式,比如:UITextFiled,UITableView等等.官方给出的解释如下: Delegation is a simple and powerful pattern in which one object in a program 1 acts on behalf of, or in coordin

IOS开发-Object-C Block的实现方式

前言:我们可以把Block当作一个闭包函数,它可以访问外部变量和局部变量,但默认是不可以修改外部变量.你可以使用它来做回调方法,比起使用代理(Delegate)会更加直观.顺带一提,苹果很多的接口(API)都使用了Block. 一.Block的基本定义 Block的基本写法(也是详细写法): returnType (^blockName)(params) = ^returnType(params) { // code... }; 中文再解释:返回类型 (^Block的名字)(Block的参数)

iOS开发-Quartz2D初识

Quartz2D如果单独的从Quartz,那么会发现Quartz是一个开源的Java作业调度框架,单独从英文翻译的角度来看的话Quartz的英文是石英,如果有的时候不小心搜索会发现手表推荐.本文中介绍的Quartz是位于MAC OS X的Drawin核心之上的绘图层,有时候也认为是CoreGraphics.Quartz直接地支持Aqua,借由显示2D绘图图形来创建用户接口,包含实时绘制(rendering)和次像素(sub-pixel)精准的反锯齿,由Quartz Compositor和Quar

iOS开发_初识视频直播

一.使用第三方ijkPlayer框架开发直播 1.去到B站得github主页,找到ijkplayer项目,下载源码 ijkplayer下载地址 2.Demo的使用以及如何编译Demo 请移步如何快速的开发一个完整的iOS直播app(播放篇) 其实里面讲的很详细,我也是参照这个写的Demo.然后在他的基础上加了一点东西.(不废话我直接上我的代码) 先看看效果图 特点 1.纯代码Masonry布局 2.集成ijkplayer第三方库,实现拉流播放 3.打包ijkplayer静态库,实现release

iOS开发——语法&amp;高级Block练习

高级Block练习 一 .最简单的block使用 使用block的三个步骤:1.定义block变量 2.创建block代码块 3.调用block匿名函数 定义一个block的构成包括:返回值,block名,参数类型. block代码块作为一个匿名函数是可以被写在其他方法中的,所以一般我们将block代码块写在其他方法里,调用该方法的时候block代码块将不会被执行,只有回调block代码块的时候,才会执行. ViewController.h 1 #import <UIKit/UIKit.h>

iOS开发:使用Block在两个界面之间传值(Block高级用法:Block传值)

使用Block的地方很多,其中传值只是其中的一小部分,下面介绍Block在两个界面之间的传值: 先说一下思想: 首先,创建两个视图控制器,在第一个视图控制器中创建一个UILabel和一个UIButton,其中UILabel是为了显示第二个视图控制器传过来的字符串,UIButton是为了push到第二个界面. 第二个界面的只有一个UITextField,是为了输入文字,当输入文字,并且返回第一个界面的时候,当第二个视图将要消失的时候,就将第二个界面上TextFiled中的文字传给第一个界面,并且显

iOS开发——代理与block传值

一.代理传值的方法 1.Hehe1ViewController.h中 #import <UIKit/UIKit.h> @protocol Hehe1ViewControllerDelegate <NSObject> - (void)backValueWith:(NSString*)str; @end @interface Hehe1ViewController : UIViewController @property(nonatomic,weak) id delegate; @en