iOS-Block

Blocks是一种语言级别的元素,可以将其当成参数值在函数之间传递的代码块。

Blocks是Objective-C对象,因此其可以被添加到集合(NSArray或NSDictionary)中。Blocks像其他语言的闭包一样,可以从包含它的区域范围内取值。

语法

Block的完整写法如下:

1
2
3
4
double (^add)(double, double) = ^(double a, double b){
return a + b;
};
NSLog(@"%f", add(1.1, 2.2));

定义中的参数如果为Void,可以不写,实现中的参数和返回如果是Void,也可以不写:

1
2
3
4
void (^haha)() = ^{
NSLog(@"Haha");
};
haha();

如果想要强调返回值类型,也可以在实现中加上返回值类型:

1
2
3
4
void (^haha)() = ^void{
NSLog(@"Haha");
};
haha();

Block可以作为函数参数传递,如果函数需要多个参数,最好是作为最后一个参数,保证函数的可读性:

1
2
3
4
5
6
- (void)testBlock:(void(^)(void))block{
block();
}
[self testBlock:^{
NSLog(@"Block..");
}];

Block还可以作为对象的属性,注意其修饰符是copy,因为其范围已经与其赋值范围不同,所以需要复制其取到的值,关于取值的范围参考下一章节:

1
@property (copy) void (^blockProperty)(void);

取值

除了从参数中取值,Blocks还可以从包含它的范围内取值。但是,其取得的值是const属性的,意外着不会改变。例如:

1
2
3
4
5
6
int a = 0;
void (^testValue)() = ^{
NSLog(@"%d", a);
};
a++;
testValue();

打印结果为:

1
0

如果是指针类型,则其指向的对象地址不变,但是对象的属性是可以发生变化的。

1
2
3
4
5
6
NSMutableArray *abc = [NSMutableArray arrayWithArray:@[@(0)]];
void (^testValue)() = ^{
NSLog(@"%d", [[abc lastObject] intValue]);
};
[abc addObject:@(1)];
testValue();

打印结果为:

1
1

如果希望取得的值是可以修改的,可以加上修饰符__block

1
2
3
4
5
6
__block int a = 0;
void (^testValue)() = ^{
NSLog(@"%d", a);
};
a++;
testValue();

打印结果为:

1
1

类型

如果需要多个Block沿用同一种语法,可以将其定义为类型:

1
2
3
4
typedef void(^VoidBlock)(void);
- (void)testBlock:(VoidBlock)block{
block();
}

定义为类型还可以避免可读性极差的Block嵌套:

1
2
3
4
void (^(^complexBlock)(void (^)(void)))(void) = ^ (void (^aBlock)(void)) {
return ^{
};
};

引用

Blocks对其取值的对象是保持强引用的,包括self,所以,容易导致循环引用:

1
2
3
self.block = ^{
[self doSomething];// capturing a strong reference to self, creates a strong reference cycle
};

解决办法是,创建一个弱引用:

1
2
3
4
__weak typeof(self) weakSelf = self;
[self testBlock:^{
weakSelf.view.backgroundColor = [UIColor blueColor];
}];