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

转载于:https://www.cnblogs.com/Jk-Chan/p/5271641.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值