一 、 什么是block?
A block is an anonymous inline collectionof code, and sometimes also called a “closure”.
--Apple
闭包是一种函数,它能够读取其它函数的内部变量。
block 也相当于内联函数,执行速度快,简单方便;
二、 为什么要使用block ?
1.使用简单、保持代码在一起、苹果推荐。。。。
2. Block除了能够定义参数列表、返回类型外,还能够获取被定义时的词法范围内的状态(比如局部变量),并且在一定条件下(比如使用__block变量)能够修改这些状态。此外,这些可修改的状态在相同词法范围内的多个block之间是共享的,即便出了该词法范围(比如栈展开,出了作用域),仍可以继续共享或者修改这些状态。
3. 一般情况,block只能访问变量,而不能修改变量的值。而要想修改变量的值,有两种方式,在定义变量的时候:
1).加static关键字;
2).加__block去修饰
三、 block 的使用
block 的简单用法:
#import "BlockTestViewController.h"
#import <stdio.h>
#import <mach/mach_time.h>
@interface BlockTestViewController ()
@property (nonatomic, copy) void (^myBlock)();//使用block 作为属性
@end
//block 定义
//无返回值 无参的block
void (^block)() = ^{
NSLog(@"block");
};
//无返回值 int 类型参数
void (^block1)(int) = ^(int n){
NSLog(@"block1 === %d",n);
};
//无返回值 block作为参数
void (^block2)(void(^block)()) = ^(void(^block)()){
NSLog(@"block2");
block();
};
//返回float block 作为参数 -- 计算block 方法执行所消耗的时间
float timeBlock(void(^block)()){
uint64_t startTime = mach_absolute_time();//获取CPU tickCount 的计数值
block();
uint64_t endTime = mach_absolute_time();
uint64_t elapsed = endTime - startTime;
return (float)elapsed / NSEC_PER_SEC;
}
@implementation BlockTestViewController
@synthesize myBlock;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.title = @"Block" ;
NSLog(@"time : %f",timeBlock(^{
int n = 10000;
for (int i = 0; i < n; i ++) {
}
}));
self.myBlock = ^{
NSLog(@"myBlock");
};
self.myBlock();
block();
block1(10);
block2(self.myBlock);
[self executeBlock:^{
NSLog(@"111111");
}completion:^(BOOL finished){
// if (finished) {//可以根据finished 属性来处理相应操作
// return ;
// }
NSLog(@"222222");
}];
}
//含有多个block 作为参数 的方法
/*
^表示是block 类型,
() 表示无参
void 表示返回值为空的block
*/
- (void)executeBlock:(void(^)())block completion:(void (^)(BOOL finished))completion{
NSLog(@"begin");
block();
NSLog(@"middle");
completion(YES);
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
如何在 block 中修改外部变量:
考虑到 block 的目的是为了支持并行编程,对于普通的 local 变量,我们就不能在 block 里面随意修改(原因很简单,block 可以被多个线程并行运行,会有问题的),而且如果你在 block 中修改普通的 local 变量,编译器也会报错。那么该如何修改外部变量呢?有两种办法,第一种是可以修改 static 全局变量;第二种是可以修改用新关键字 __block 修饰的变量
__block int i = 0;
static int j = 0;
void (^block4)() = ^(){
i = i + 1;
j = j+ 2;
};
block4();
NSLog(@"i = %d, j = %d", i , j);//i = 1; j = 2
block 数组:
void (^blocks[2])(void) = {
^(void){
NSLog(@"block[0]");
}, //注意逗号
^(void){
NSLog(@"block[1]");
}
};
调用方式:
blocks[0]();
blocks[1]();
// blocks[2]();//当然也会有数组越界
当然也会存在数组越界的行为,如果在blocks[2]中我们即使生命了两个但只实现了一个block的实现,当调用block[1]的时候同样也会发生数组越界的行为;
四、 使用规则
block在使用过程中要避免引用环的出现, 如下例子,在block 中使用self 此时,block会retain self 对象,使self的引用计数加1,从而会永远无法执行dealloc 方法,因为在self 与block 之间产生了引用环;
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url] ;
[request setCompletionBlock:^{
[self imageDidFinishLoading:request];
}];
解决方法:若引用self , 此时self 在block 中不会被计数 +1
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url] ;
__block ClASS *weakSelf = self;
[request setCompletionBlock:^{
[self imageDidFinishLoading:request];
}];
谨记!
block 是分配在 stack 上的,这意味着我们必须小心里处理 block 的生命周期。比如如下的做法是不对的,因为 stack 分配的 block 在 if 或 else 内是有效的,但是到大括号 } 退出时就可能无效了:
void (^block3)(void) = 0;
if (true) {
block3 = ^{ printf("true\n"); };
} else {
block3 = ^{ printf("false\n"); };
}
block3();
在block函数体中使用的实例变量将自动retained ,体会:目的是防止return时候没有返回真正的带有空间地址的变量;
eg:
如果我们在Objective-C的方法中使用block时,以下几个和记忆体管理的事是需要额外注意的:
l 若直接存取实体变数(instance variable),self的参考计数将被加1。
l 若透过变数存取实体变数的值,则只变数的参考计数将被加1。
以下程式码说明上面两种情况,在这个假设instanceVariable是实体变数:
1: dispatch_async (queue, ^{
2: // 因为直接存取实体变数instanceVariable ,所以self 的retain count 会加1
3: doSomethingWithObject (instanceVariable);
4: });
5: id localVaribale = instanceVariable;
6: dispatch_async (queue, ^{
7: //localVariable 是存取值,所以这时只有localVariable 的retain count 加1
8: //self 的 return count 并不会增加。
9: doSomethingWithObject (localVaribale);
10: });
未完待续。。。