在objc中使用Block

一个完整的Block由声明和主体两部分组成,这种形式与定义一个JaveScript函数类似。但由于Block在其语法内部使用^符号,使其整体看起来比较古怪。

NSInteger x = 123;
void (^printXAndY)(NSInteger) = ^(NSInteger y) {
    NSLog(@"%d %d\n", x, y);
};
printXAndY(456); // prints: 123 456

上面是一个带有简单输出功能的Block,= 号的左侧为Block的声明部分( void (^printXAndY)(NSInteger) )
该声明从左至右依次为 返回值类型 、 Block的名称 和 输入参数类型 , 并用括号进行了分割。在这个Block声明里,我们能看出该Block声明的返回值为空,名称为printXAndY,传入参数为整形。事实上,除了传入参数数量或是返回值类型可能不同外,每个Block声明的写法大致也就是这样子。

= 号右侧是Block的主体部分,在该示例里,主题部分有一个整形的输入参数y(与左侧声明相对应),在其右侧的大括号是主题部分的逻辑实现,类似于函数的大括号部分,因为没有返回值,所以没有写return,只做了一个简单的输出。

__block

在Block里,外部变量以Copy的形式复制到Block的内部,Block没有对外部变量的修改权(若是修改,在编译时会提示错误)。所以,如果在Block执行前外部变量发生变化,Block内部该值不会随之改变:

NSInteger x = 123;
void (^printXAndY)(NSInteger) = ^(NSInteger y) {
    x = x + y; // error
    NSLog(@"%d %d\n", x, y);
};
x = 789;
printXAndY(456); // prints: 123 456

但是,Block提供了 __block 类型的变量声明,该声明可以使变量与Block绑定在一起,这样当外部变量发生改变时,Block内部变量值也会一起变化:

__block NSInteger x = 123;
void (^printXAndY)(NSInteger) = ^(NSInteger y) {
    NSLog(@"%d %d\n", x, y);
};
x = 789;
printXAndY(456); // prints: 789 456

引用声明

除了上述特点之外,Block用到最多的就是其提供的与JS闭包和匿名表达式类似的语法操作。如果熟知JS语法,一定会对其闭包和匿名表达式的灵活使用印象深刻。

在Block里,声明和主体部分可以单独拆分使用,拆分后的Block声明相当于一个带有输入参数和返回值的模板类型,比如我们常用的动画API就使用了Block:

//动画函数声明
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations //...

从代码可以看出,函数参数是 void (^)(void),这是一个匿名的Block声明。当动画函数调用时,我们传递一个与之匹配的Block主体:

//动画函数调用
[UIView animateWithDuration:5.0f animations:^{
    label.alpha = 0.5f;
}];

为了使代码结构更加清晰,我们可以直接声明一个Block类型:

typedef void (^TableViewCellConfigureBlock)(id cell, id item);

然后在使用时进行Block参数传递:

//传递参数
void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) {
    cell.label.text = photo.name;
};
photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos
                                            cellIdentifier:PhotoCellIdentifier
                                        configureCellBlock:configureCell];
self.tableView.dataSource = photosArrayDataSource;

//执行Block
self.configureCellBlock(cell, item);

从中我们可以看出Block函数能像JS里的闭包和匿名函数一样灵活传递和使用,填补了objc语法的缺陷。

参见

Comments