block浅析与使用block导致循环强引用举例
Posted Jk_Chan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了block浅析与使用block导致循环强引用举例相关的知识,希望对你有一定的参考价值。
- 定义语法
- 外部变量值截获
- 改变外部变量值
- block变量作用域导致block体从栈复制到堆同时变量持有了block体中截获的变量
- block变量作用域导致强引用
typedef void(^BlockType)(NSString *str);//声明一个void(^)(NSString *str)的block类型,类型名为BlockType
-(void)viewDidLoad
{
- int i=[self getValuesFrom:^int(int i, int y) {//算法由自己定,值由别人传入
- return i+y;
- }];//拿到经自己算法的结果
- NSLog(@"i=%d",i);
- __block int test=100;
- NSLog(@"test is:%d",test);//100
- void(^block1)()=^{
- NSLog(@"block1截获的test is:%d",test);
- test=110;
- };
- NSLog(@"test is:%d",test);//100
- test+=1;
- NSLog(@"test is:%d",test);//101
- void(^block2)()=^{
- NSLog(@"block2截获的test is:%d",test);
- test=120;
- };
- NSLog(@"test is:%d",test);//101
- test+=1;
- NSLog(@"test is:%d",test);//102
- block1();//执行了block1,输出test为102,把test的值改变为110
- NSLog(@"test is:%d",test);//110
- test+=1;
- NSLog(@"test is:%d",test);//111
- block2();//执行了block2,输出test为111,把test的值改变为120
- NSLog(@"test is:%d",test);//120
- test+=1;
- NSLog(@"test is:%d",test);//121
- /*
- 得出结论,block截获外部变量值不是看block体实现的时候截获的,而是在执行block的时候,会把执行时当前外部变量值截获,
- 所以不存在担心实现的时候外部变量是这个值,到执行block的时候外部变量值变成另外的,即使多个block用到了同一个外部变量值,因为无论多少个地方用到了,同一刻操作该变量的只能有一个地方,并且block是在执行时才截获该值。不过也应注意外部变量值的截获是在
- 执行时才截获这个原则。
- block内对外部变量的赋值操作会影响外部变量的实际结果。
- */
- void(^testIsCanHoldSelf)()=^{
- NSLog(@"self is %@",self);
- };
- testIsCanHoldSelf();
- /*
- 不会引起强引用,因为testIsCanHoldSelf的作用域为viewDidLoad函数,
- testIsCanHoldSelf变量被废弃时,testIsCanHoldSelf截获的self持有权也会被释放
- 若把testIsCanHoldSelf声明为类的strong成员变量,就会导致循环引用,因为
- testIsCanHoldSelf为强引用变量的话,block体在赋值给testIsCanHoldSelf的时候,block体
- 从栈复制到堆,block体截获了self或者只截获了self中的成员变量,
- testIsCanHoldSelf也持有self,
- self又持有testIsCanHold成员变量.
- 如果想通过把testIsCanHoldSelf声明为__weak类型的话,那么block体在生成的时候赋值给
- __weak 的testIsCanHoldSelf,没变量持有该block体,那么该block体马上又被释放了,
- (可以把block体当为对象考虑)
- 解决该强引用的问题,让testIsCanHoldSelf变量不持有self即可,但是block需要用到self
- 所以可以让block体截获一个__weak的self
- __weak ViewController *weakSelf=self;
- testIsCanHoldSelf=^{
- NSLog(@"self is %@",weakSelf);
- };
- testIsCanHoldSelf();
- */
}
-(NSString *)testBlock{
- NSString *(^myBlock)(NSString *str)= ^/*NSString **/(NSString *str){
- return str;
- };//内嵌方法
- return myBlock(@"testValues");
}
-(int)getValuesFrom:(int(^)(int i,int y))block{
- return block(1,2);//block体内调用block
}
循环强引用举例以及解决方案:
- EOCNetworkFetcher持有completion对象NSData对象,NSURL对象
- EOCClass持有EOCNetworkFetcher对象,NSdata对象,
- 生成completion栈对象捕获EOCClass中的NSData对象
- 也就是间接捕获了self,
- completion栈对象传入EOCNetworkFetcher对象被它的completion对象持有
- EOCNetworkFetcher会把EOCClass的completion栈对象copy到堆中
- 从而EOCNetworkFetcher对象因持有completion堆对象而持有EOCClass对象
- 从而发生循环强引用。
解决办法有两种:
- 1.如果EOCClass中的EOCNetworkFetcher对象的持有者只是它本身,
- 那么释放持有EOCNetworkFetcher对象,EOCNetworkFetcher对象没有了持有者,
- 之后就会释放内存,从而也会释放EOCNetworkFetcher对象里面的成员变量,从而
- EOCNetworkFetcher对象里面的completion对象不持有EOCClass中的completion,从而
- completion对象没有持有者便释放内存从而也会释放所捕获的EOCClass对象
- 从而循环强引用被打破了。
- 2.如果EOCClass传入到EOCNetworkFetcher对象中的completion对象只有EOCNetworkFetcher持有,
- 那么让EOCNetworkFetcher对象不再持有传入的completion,从而不再持有completion捕获的EOCClass对象
- 从而循环强引用被打破了,也就是在适当的地方把EOCNetworkFetcher对象中的completion对象释放置为nil即可
- 一般采用第二种,让提供API方来处理强引用问题而不是使用API方来处理
- 如果把EOCClass中的completion捕获的对象为__weak变量呢?
- 如果该__weak变量是在方法体中声明,那么EOCClass中的方法体执行完,__weak变量作用域超过了就释放对象
- 所以EOCNetworkFetcher对象无法赋值EOCClass对象。
- 如果该__weak变量是EOCClass对象的成员变量,那么传入到EOCNetworkFetcher中的时候变量还没被释放,EOCNetworkFetcher
- 可以给EOCClass对象__weak成员变量所指的内存赋值,好像可以解决问题,但是实际上completion捕获的__weak对象
- 需要通过EOCClass对象的self引用,此时已经捕获了EOCClass对象,所以还是解决不了问题。
- 又或者EOCNetworkFetcher对象中的completion变量为__weak呢?
- 这样EOCClass中的completion传入进来后,超过了作用域,EOCClass中的completion就被释放
- 而EOCNetworkFetcher对象中的completion变量又没有持有接收到的completion对象,所以completion对象内存释放了
- 导致EOCNetworkFetcher对象中的completion变量永远为nil
以上是关于block浅析与使用block导致循环强引用举例的主要内容,如果未能解决你的问题,请参考以下文章