block浅析与使用block导致循环强引用举例

Posted Jk_Chan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了block浅析与使用block导致循环强引用举例相关的知识,希望对你有一定的参考价值。

  1. 定义语法
  2. 外部变量值截获
  3. 改变外部变量值
  4. block变量作用域导致block体从栈复制到堆同时变量持有了block体中截获的变量
  5. 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导致循环强引用举例的主要内容,如果未能解决你的问题,请参考以下文章

Block 循环引用

IOS block 对象强引用和若引用

block-循环引用

Block循环引用问题

block强弱引用

block 与delegate( weak/id)