一、什么是Block?
Block是将函数及其执行上下文封装起来的对象。
比如:
NSIntegernum =3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){
return n*num;
};
block(2);
二、Block变量截获
1、局部变量截获 是值截获。
比如:
NSIntegernum =3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){
return n*num;
};
num =1;
NSLog(@"%zd",block(2));
这里的输出是6而不是2,原因就是对局部变量num的截获是值截获。
同样,在block里如果修改变量num,也是无效的,甚至编译器会报错。
NSMutableArray* arr = [NSMutableArray arrayWithObjects:@"1",@"2",nil];
void(^block)(void) = ^{
NSLog(@"%@",arr);//局部变量
[arr addObject:@"4"];
};
[arr addObject:@"3"];
arr =nil;
block();
打印为1,2,3
局部对象变量也是一样,截获的是值,而不是指针,在外部将其置为nil,对block没有影响,而该对象调用方法会影响
2、局部静态变量截获 是指针截获。
static NSInteger num =3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){
return n*num;
};
num =1;
NSLog(@"%zd",block(2));
输出为2,意味着num = 1这里的修改num值是有效的,即是指针截获。
同样,在block里去修改变量m,也是有效的。
三、Block的几种形式
分为全局Block(_NSConcreteGlobalBlock)、栈Block(_NSConcreteStackBlock)、堆Block(_NSConcreteMallocBlock)三种形式
其中栈Block存储在栈(stack)区,堆Block存储在堆(heap)区,全局Block存储在已初始化数据(.data)区
1、不使用外部变量的block是全局block
比如:
NSLog(@"%@",[^{
NSLog(@"globalBlock");
}class]);
输出:
__NSGlobalBlock__
2、使用外部变量并且未进行copy操作的block是栈block
比如:
NSInteger num =10;
NSLog(@"%@",[^{
NSLog(@"stackBlock:%zd",num);
}class]);
输出:
__NSStackBlock__
日常开发常用于这种情况:
[self testWithBlock:^{
NSLog(@"%@",self);
}];
- (void)testWithBlock:(dispatch_block_t)block {
block();
NSLog(@"%@",[block class]);
}
3、对栈block进行copy操作,就是堆block,而对全局block进行copy,仍是全局block
比如堆1中的全局进行copy操作即赋值:
void(^globalBlock)(void) = ^{
NSLog(@"globalBlock");
};
NSLog(@"%@",[globalBlock class]);
输出:
__NSGlobalBlock__
仍是全局block
而对2中的栈block进行赋值操作:
NSInteger num =10;
void(^mallocBlock)(void) = ^{
NSLog(@"stackBlock:%zd",num);
};
NSLog(@"%@",[mallocBlock class]);
输出:
__NSMallocBlock__
对栈blockcopy之后,并不代表着栈block就消失了,左边的mallock是堆block,右边被copy的仍是栈block
比如:
[self testWithBlock:^{
NSLog(@"%@",self);
}];
- (void)testWithBlock:(dispatch_block_t)block{
block();
dispatch_block_t tempBlock = block;
NSLog(@"%@,%@",[block class],[tempBlock class]);
}
输出:
__NSStackBlock__ , __NSMallocBlock__
即如果对栈Block进行copy,将会copy到堆区,对堆Block进行copy,将会增加引用计数,对全局Block进行copy,因为是已经初始化的,所以什么也不做。
另外,__block变量在copy时,由于__forwarding的存在,栈上的__forwarding指针会指向堆上的__forwarding变量,而堆上的__forwarding指针指向其自身,所以,如果对__block的修改,实际上是在修改堆上的__block变量。
即__forwarding指针存在的意义就是,无论在任何内存位置, 都可以顺利地访问同一个__block变量。
另外由于block捕获的__block修饰的变量会去持有变量,那么如果用__block修饰self,且self持有block,并且block内部使用到__block修饰的self时,就会造成多循环引用,即self持有block,block 持有__block变量,而__block变量持有self,造成内存泄漏。
比如:
__block typeof(self) weakSelf =self;
_testBlock = ^{
NSLog(@"%@",weakSelf);
};
_testBlock();
如果要解决这种循环引用,可以主动断开__block变量对self的持有,即在block内部使用完weakself后,将其置为nil,但这种方式有个问题,如果block一直不被调用,那么循环引用将一直存在。
网友评论