主要讲解 Block 的基础知识
Block部分一
Block部分二
Block部分三
Block知识点总结
1. 什么是Block?
Block
是一个对象, 对象中封装了一个函数以及函数执行的上下文;Block
的调用本质是函数
的调用;
2. Block截获变量
请问以下代码执行的结果?
///局部变量: 基本数据类型
- (void)block1 {
NSInteger count = 10;
NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
return count * num;
};
count = 50;
NSLog(@"局部变量: 基本数据类型 结果是: %ld", Block(2));
#局部变量: 基本数据类型 结果是: 20
__block NSInteger count1 = 10;
NSInteger (^Block1)(NSInteger) = ^NSInteger(NSInteger num){
return count1 * num;
};
count1 = 50;
NSLog(@"用__block修饰的局部变量: 基本数据类型 结果是: %ld", Block1(2));
#用__block修饰的局部变量: 基本数据类型 结果是: 100
}
///静态局部变量
- (void)block3 {
static NSInteger count = 9;
NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
return count * num;
};
count = 10;
NSLog(@"静态局部变量 结果是: %ld", Block(2));
}
#静态局部变量 结果是: 20
///全局变量
- (void)block4 {
//NSInteger globalCount = 11;
NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
return globalCount * num;
};
globalCount = 10;
NSLog(@"全局变量 结果是: %ld", Block(2));
}
#全局变量 结果是: 20
///全局静态变量
- (void)block5 {
// static const NSInteger staticGlobalCount = 22;
NSInteger (^Block)(NSInteger) = ^NSInteger(NSInteger num){
return staticGlobalCount * num;
};
staticGlobalCount = 33;
NSLog(@"全局静态变量 结果是: %ld", Block(2));
}
#全局静态变量 结果是: 66
///局部变量: 对象
- (void)block2 {
__block Man *man = [[Man alloc] init];
NSString* (^Block)(NSString*) = ^NSString* (NSString *name){
man.name = name;
return man.name;
};
NSLog(@"局部变量: 对象 结果是: %@", Block(@"Zhangsan"));
}
什么情况下要使用
__block
修饰符?
一般情况下要对被截获变量进行赋值
(不是使用
)操作时需要添加__block
修饰符;
- 无论是
对象
还是基本数据类型
如果是局部变量
, 在block
中修改,都需要用__block
修饰;静态局部变量
,静态全局变量
,全局变量
不需要__block
修饰就能在block
更改;
///block中对对象的使用
- (void)testBlcok1 {
NSMutableArray *arr = [NSMutableArray arrayWithCapacity:0];
void(^Block)(void) = ^void(void){
///这里是对arr的使用, 而不是赋值, 所以不用__block修饰;
[arr addObject:@"1"];
};
Block();
NSLog(@"block中对对象的使用 数组中内容为: %@", arr);
}
#block中对对象的使用 数组中内容为: (1)
///block中对对象的赋值
NSMutableArray *arr1 = nil;
void(^Block1)(void) = ^void(void){
///这里是对arr的赋值, 要用__block修饰, 不然会报错;
arr = [NSMutableArray arrayWithCapacity:0];
};
Block1();
结论:
- 对于
基本数据
类型的局部变量
时截获它的值; - 对于对象的
局部变量
或者成员变量
是连同所有权修饰符
一起截获; - 以
指针形式
截获局部静态变量; -
不截获
全局变量和静态全局变量;
3. Block内存管理
Block
分为三种;
- NSStack`Block 栈区
- NS
Malloc
Block 堆区 - NS
Global
Block 全局
Block
的copy
操作
Block类别 | 源 | copy结果 |
---|---|---|
_NSStack Block |
栈区 | 堆区 |
_NSMalloc Block |
堆区 | 增加引用计数 |
_NSGlobal Block |
数据区 | 什么都不做 |
4. Block的循环引用
4.1 自循环
///循环引用---自循环
- (void)circleReferenceSelf {
_array = [NSMutableArray arrayWithCapacity:0];
[_array addObject:@"Test"];
_strBlock = ^NSString*(NSString *str) {
NSString *resultStr = [NSString stringWithFormat:@"result is : %@", _array.firstObject];
return resultStr;
};
NSLog(@"%@", _strBlock(@"HongKong"));
}
///不会产生自循环
- (void)notCircleReferenceSelf {
_array = [NSMutableArray arrayWithCapacity:0];
[_array addObject:@"Test"];
__weak NSMutableArray *tempArr = _array;
_strBlock = ^NSString*(NSString *str) {
NSString *resultStr = [NSString stringWithFormat:@"result is : %@", tempArr.firstObject];
return resultStr;
};
NSLog(@"%@", _strBlock(@"HongKong"));
}
#解除方案:使用__weak修饰临时变量指向_array; 原理是在下面的第二步中将stong(强引用)变为了weak(弱引用);
为什么会造成自循环?
当前类
用copy
修饰_strBlock
, 所以对其有强引用;- 上面有提到
对于对象的局部变量或者成员变量是连同所有权修饰符一起截获
;因为_array
使用strong
修饰, 所以_strBlock
中有一个strong
类型的指针指向当前类
;- 造成自循环;
4.2 多循环
///循环引用----多循环引用(类似)
- (void)bigCircleReference {
__block ViewController *weakSelf = self;
_bigMan = [[Man alloc] init];
_bigMan.intBlock = ^NSInteger(NSInteger num) {
return weakSelf.index * num;
};
NSLog(@"会多循环引用: %ld", _bigMan.intBlock(100));
}
#注意: 这段代码在MRC下不会有任何问题; 但是ARC下会循环引用;
//.不会产生多环引用
- (void)notBigCircleReference {
__block ViewController *weakSelf = self;
_bigMan = [[Man alloc] init];
_bigMan.intBlock = ^NSInteger(NSInteger num) {
NSInteger temp = weakSelf.index * num;
weakSelf = nil;
return temp;
};
NSLog(@"不会多循环引用: %ld", _bigMan.intBlock(100));
}
#方案:通过断环方式或者开发中避免;
为什么会循环引用?
- 当前类用
strong
修饰_bigMan
, 所以对其有强引用;Man
类中用copy
修饰_intBlock
, 所以对其有强引用;- 从上篇文章中我们知道
ARC下, __block修饰的对象会被强引用
; 所以在_intBlock
内部会有对当前类强引用;- 造成多环循环引用;
文中示例代码
参考文章
iOS block-变量的捕获(capture)