关于 Block
在 iOS4.0之 后,block 横空出世,它本身封装了一段代码并将这段代码当做变量,通过 block()
的方式进行回调。这不免让我们想到在C函数中,我们可以定义一个指向函数的指针并且调用:
bool executeSomeTask(void) { } bool (*taskPoint)(void); taskPoint = something;
|
上面的函数指针可以直接通过 (*taskPoint)()
的方式调用 executeSomeTask
这个函数,这样对比 block 跟似乎 C 语言的函数指针是一样的,但是两者仍然存在以下区别:
- block 的代码是内联的,效率高于函数调用
- block 对于外部变量默认是只读属性
- block 被 Objective-C 看成是对象处理
Block 特性
认识 Block
Block 代码结构如下:
先来看一个简单的block吧:
BOOL (^isInputEven)(int) = ^(int input) { if (input % 2 == 0) { return YES; } else { return NO; } };
|
以上定义了一个 block 变量,block 本身就是一个程序段,因此有返回值有输入参数,这里这个 block 返回的类型为 BOOL
。^
符号表示 block 定义的开始,block 的名称紧跟在^
符号之后,这里block的名称是 isInputEven
。这段 block 接受一个 int
型的参数,而在等号后面的 int input
是对这个传入 int 参数的说明:在该 block 内,将使用 inpu t这个名字来指代传入的 int 参数。
调用这个block的方法就非常简单和直观了,类似调用c函数的方式即可:
int x = -101; NSLog(@"%d %@ number", x, isInputEven(x) ? @"is an even" : @"is not an even");
|
不出意外的话输出为 -101 is not an even number
。
Block 递归调用
Block 想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候 Block 变量就初始化了,可以递归调用。
static void (^ const blocks)(int) = ^(int i) { if (i > 0) { NSLog(@"num:%d", i); blocks(i - 1); } }; blocks(3);
|
打印结果如下:
在 Block 中使用局部变量和全局变量
使用全局变量
int global = 1000; int main(int argc, const char * argv[]) { @autoreleasepool { void(^block)(void) = ^(void) { global++; NSLog(@"global:%d", global); }; block(); NSLog(@"global:%d", global); } return 0; }
|
打印结果如下:
而局部变量可以使用,但是不能改变。
int local = 500; void(^block)(void) = ^(void) { local++; NSLog(@"local:%d", local); }; block(); NSLog(@"local:%d", local);
|
在代码块中改变局部变量编译不通过。怎么在代码块中改变局部变量呢?在局部变量前面加上关键字:__block
__block int local = 500; void(^block)(void) = ^(void) { local++; NSLog(@"local:%d", local); }; block(); NSLog(@"local:%d", local);
|
运行结果:
Block 回调
先定义 RootViewController,初始化一个 UILabel 和 UIButton,点击 Button 页面跳转到 SecViewController,在 SecViewController 文本框内输入文字并返回到 RootViewController,此时在 SecViewController 页面输入的文本展示到 RootViewController 上。
需要回调数据的是 Root 视图,那么 Block 就应该在 SecView 中定义,用于获取传入回调数据。
我们在 SecViewController.h 定义了 typedef void (^BlockValue)(NSString *value)
的别名为 BlockValue
。
#import <UIKit/UIKit.h> typedef void (^BlockValue)(NSString *value); @interface SecViewController : UIViewController @property (copy, nonatomic) BlockValue myBlock; @end
|
点击 Sec 页面的按钮时添加 BlockValue
的传参操作:
-(void)btnClicked{ self.myBlock(self.textField.text); [self dismissViewControllerAnimated:YES completion:nil]; }
|
这样我们就可以在想要获取数据回调的地方,也就 RootViewController 的视图中调用 block:
-(void)btnClicked{ SecViewController *sec = [[SecViewController alloc]init]; __weak RootViewController *weakSelf = self; sec.myBlock = ^(NSString *value) { weakSelf.label.text = value; }; [self presentViewController:sec animated:YES completion:nil]; }
|
效果如下:
微信扫一扫,向我赞赏
支付宝扫一扫,向我赞赏