Block原理、变量截获、三种形式、Block中weak/strong self的用法
前言
block虽然经常使用,但是对于其中的原理和一些特性不是很明白,查找了一些资料,算是一次深入的了解和学习,结合自己的理解和相关资料,写一篇总结,以便以后温故,错误或不足的地方希望大家多多指正,一起讨论学习。
1、Block的原理
网上资料解释:
1、Block是将函数及其执行上下文封装起来的对象。
2、带有自动变量(局部变量)的匿名函数。它是C语言的扩充功能。之所以是拓展,是因为C语言不允许存在这样匿名函数。
3、block本质上也是一个oc对象或者说是一个结构体,内部也有一个isa指针。block是封装了函数调用(函数指针)以及函数调用环境(捕获到的参数)的OC对象。
自己的理解:Block是一个封装了变量和方法的对象,可以将其与类进行类比,变量相当于属性,方法相当于类里面的方法或者函数。一般使用较多的是当做属性使用,可以理解为是一个类对象。
2、变量截获
局部变量
静态变量
静态全局变量
全局变量
1、局部变量截获是值
截获
NSInteger num = 3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n*num;
};
num = 1;
NSLog(@"%zd",block(2));
自己尝试打印下结果,同时尝试下在block内部修改值看看结果。
局部对象变量也是一样,截获的是值
NSMutableArray * arr = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
void(^block)(void) = ^{
NSLog(@"%@",arr);//局部变量
[arr addObject:@"4"];
};
[arr addObject:@"3"];
arr = nil;
block();
2、局部静态变量截获是指针
截获。
static NSInteger num = 3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n*num;
};
num = 1;
NSLog(@"%zd",block(2));
3、全局变量,静态全局变量截获:不截获,直接取值
。
//静态全局变量
static NSInteger num3 = 300;
//全局变量
NSInteger num4 = 3000;
//局部变量
NSInteger num1 = 30;
//静态局部变量
static NSInteger num2 = 3;
void(^block3)(void) = ^{
NSLog(@"%zd",num1);//局部变量
NSLog(@"%zd",num2);//局部静态变量
NSLog(@"%zd",num3);//全局静态变量
NSLog(@"%zd",num4);//全局变量
};
block3();
4、__block修饰变量截获是指针
截获。
__block NSInteger num5 = 30000;
void(^block3)(void) = ^{
NSLog(@"%zd",num5);//__block修饰变量
};
num5 = 2800;
block3();
3、Block的3种形式
分为全局Block(_NSConcreteGlobalBlock)、栈Block(_NSConcreteStackBlock)、堆Block(_NSConcreteMallocBlock)三种形式
1、不使用外部变量的block是全局block
NSLog(@"%@",[^{
NSLog(@"globalBlock");
} class]);
2、使用外部变量并且未进行copy操作的block是栈block,日常开发常用于这种情况
。
NSInteger num = 10;
NSLog(@"%@",[^{
NSLog(@"stackBlock:%zd",num);
} 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
对栈block copy之后,并不代表着栈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
4、weak/strong self的用法
1、weakSelf ;
场景:self 、person、Pblock(person中的block)
self持有person对象,person对象持有Pblock,Pblock中使用到self,对self进行截获,进而持有self,最终结果是:死循环
,造成内存泄漏。
使用__weak __typeof(self)weakSelf = self
弱化self对person的持有。
weakSelf解决循环引用
2、strongSelf ;
block截获的变量在超出其作用域后仍能使用(比如block截获了self,然后block又被传递到其它地方使用,此时self按理已经释放)。这其实是因为系统会自动的根据情况将block从栈拷贝到堆中并强引用它截获的变量(一般我们的block最开始都是在栈中的),我们知道栈的内存是由系统管理的,而堆是由程序猿管理的,所以实现了变量超出作用域仍能使用。如果block没有拷贝到堆上,超出作用域self被释放,如果再次使用可能会导致crash。
__weak __typeof(self)weakSelf = self;
blk_t blk = ^() {
__strong __typeof(weakSelf)strongSelf = weakSelf;
}
strongSelf是为了保证任何情况下self在超出作用域后仍能够使用
下面列出block能够拷贝的情况:
1、调用block的copy方法
2、block作为返回值
3、block赋值时
4、Cocoa框架中方法名中含有usingBlock的方法
5、GCD中
weakSelf是为了解决循环引用
strongSelf是为了保证任何情况下self在超出作用域后仍能够使用
总结:通过学习对block有了深入的了解,在后面的开发中加以灵活使用,进而不断加深对block的理解和研究。至于特别底层的原理实在难以消化,再接再厉吧。
参考链接:
1、https://www.jianshu.com/p/f539c017b0c8
2、https://www.jianshu.com/p/bcd494ba0e22
3、https://blog.csdn.net/u014600626/article/details/78697535
4、Block中weak/strong self的用法
侵权删除!