对象A持有对象B,调用B的block参数方法,在里面使用了self。在使用block我们都会默认在里面使用weakself,网上搜了很多解释都是为了防止循环引用,以防self被持有导致内存泄露。

那么问题来了,到底是谁持有了self?我以前没有深究,一直以为是A和B互相持有导致的循环引用。

但是最近正好有重新深究block,于是便想着从block里找到答案。首先写一段代码,将其从oc转换成c++

@implementation C
-(void)test {}
-(void) methodA{[self methodB:^{[self test];}];
}
-(void):(void(^)(void))handler {handler();
}
@end
clang -rewrite-objc xx.m

查看转换后的代码,可以看到,block就是一个结构体,因为在里面使用了self,所以结构体就增加了一个C *self。methodA方法里调用了methodB方法,创建了一个block结构体变量作为参数传了进去,就是这个结构体变量持有了self。

相当于A调用B的block方法,里面使用了self,创建了一个临时的block变量持有self,self不是被B持有,而是被一个临时变量持有。正常情况下,methodB调用完毕后,这个block变量自然就会被释放,self也就不再被它持有了。但有些情况就是,这个block变量会被保留下来,从而导致self内存泄露的原因。

//block结构体
struct __C__methodA_block_impl_0 {struct __block_impl impl;struct __C__methodA_block_desc_0* Desc;C *self;__C__methodA_block_impl_0(void *fp, struct __C__methodA_block_desc_0 *desc, C *_self, int flags=0) : self(_self) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};//methodA
static void _I_C_methodA(C * self, SEL _cmd) {((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("methodB:"), ((void (*)())&__C__methodA_block_impl_0((void *)__C__methodA_block_func_0, &__C__methodA_block_desc_0_DATA, self, 570425344)));
}//methodB
static void _I_C_methodB_(C * self, SEL _cmd, void (*handler)()) {((void (*)(__block_impl *))((__block_impl *)handler)->FuncPtr)((__block_impl *)handler);
}

具体有两种情况会导致self内存泄露:

1.循环引用,所谓的循环引用不是A<->B互相引用,而是A->B->block->A。

2.持有block的对象存活时间要比self长,比如类对象或者单例对象等。

所以建议使用block还是要weakself,因为你无法保证传过去的block是否在方法的另一边被保留下来了。比如常见的NSTimer,就是因为self被block持有,block被NSTimer类对象持有。

下面几种block情况:

@implementation A- (instancetype)initWithName:(NSString *)name {if (self = [super init]) {_name = name;}return self;
}- (void)dealloc {NSLog(@"%@ dealloc", self.name);
}//内存释放
//调用B的block参数方法,B不保存block,A不保存B
- (void)B_Method1 {B *b = [[B alloc] init];[b method1:^{[self func];NSLog(@"invoke B Method1, but dont retain B");}];
}//内存释放
//调用B的block参数方法,B保存block,A不保存B
- (void)B_Method2 {B *b = [[B alloc] init];[b method2:^{[self func];NSLog(@"invoke B Method2, but dont retain B");}];
}//内存释放
//调用B的block参数方法,B不保存block,A保存B对象
- (void)B_Method1_RetainB {B *b = [[B alloc] init];[b method1:^{[self func];NSLog(@"invoke B Method1, and retain B");}];self.b = b;
}//内存泄露
//调用B的block参数方法,B保存block,A保存B对象
- (void)B_Method2_RetainB {B *b = [[B alloc] init];[b method2:^{[self func];NSLog(@"invoke B Method2, and retain B");}];self.b = b;
}//内存释放
//调用B的block参数 类方法,B不保存block
- (void)B_ClassMethod1 {[B classMethod1:^{[self func];NSLog(@"invoke B ClassMethod1");}];
}//内存泄露
//调用B的block参数 类方法,B保存block
- (void)B_ClassMethod2 {[B classMethod2:^{[self func];NSLog(@"invoke B ClassMethod1");}];
}- (void)func {}@end

iOS block循环引用问题深究相关推荐

  1. iOS开发笔记(二):block循环引用

    写这篇文章的缘由是第一次面试时被问到了block循环引用的问题,当时回答的不是很好,首先要明确的是,block是否用copy修饰决定不了循环引用的产生,在此再一次进行补强,有不对的地方还请多多指教. ...

  2. Block循环引用问题(Objective-c)

    造成循环引用的简单理解是:Block的拥有者在Block作用域内部又引用了自己,因此导致了Block的拥有者永远无法释放内存,就出现了循环引用的内存泄漏 示例代码 @interface ObjTest ...

  3. 和block循环引用说再见

    to be block? or to be delegate? 这是一个钻石恒久远的问题.个人在编码中暂时没有发现两者不能通用的地方,习惯上更偏向于block,没有什么很深刻的原因,只是认为block ...

  4. ios开发 循环引用 检测_iOS开发——Block引起循环引用的解决方案

    [目前总结了一下,在iOS平台容易引起循环引用的几个场景:一.parent-child模式二.block(编译器会提示警告)三.NSTimer] up vote 0 down vote favorit ...

  5. iOS Block弱引用

    先weak再strong.可以很好的管理Block内部对self的引用 常规写法 __weak typeof(self) weakSelf = self;self.Button.rac_command ...

  6. Block的循环引用

    2019独角兽企业重金招聘Python工程师标准>>> 在ios常见的循环引用中曾经提到过block: 看看上面最基本的block循环应用,self包含block,block包含了s ...

  7. block为什么用copy以及如何解决循环引用

    在完成项目期间,不可避免的会使用到block,因为block有着比delegate和notification可读性更高,而且看起来代码也会很简洁.于是在目前的项目中大量的使用block. 之前给大家介 ...

  8. 相亲app开发,解决内存循环引用的问题

    循环引用是什么 ARC已经出来很久了,自动释放内存的确很方便,但是在相亲app开发应用中,并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是--循环引用.循环引用可以简单理 ...

  9. iOS进阶之底层原理-block本质、block的签名、__block、如何避免循环引用

    面试的时候,经常会问到block,学完本篇文章,搞通底层block的实现,那么都不是问题了. block的源码是在libclosure中. 我们带着问题来解析源码: blcok的本质是什么 block ...

最新文章

  1. 影响几代产品人的宝典第 2 部开启预售!5 折限量抢,产品人都需要一本
  2. java bytebuffer 大小,bytebuffer 获取长度
  3. Linux内核分析作业第二周
  4. linux 安装tomcat遇到的问题
  5. c++ 门面模式(Facade)
  6. Python实现web动态服务器
  7. SAP License:IMG子菜单
  8. Python连接mysql密码用密文_druid配置数据库连接使用密文密码
  9. 为什么要用C语言实现面向对象
  10. Java Web底层(1)
  11. 计算机游戏优化,Win10电脑玩游戏优化方法!必须关闭这几个设置,效果提升巨大...
  12. Mac系统投屏到电视机的方法
  13. 墨羽卿画第二章第7节:跬步
  14. 最好的时光在路上,最美的风景在远方
  15. Android自定义View使用总结
  16. 基于加密机制的WebService访问与通信安全
  17. 双臂电桥测低电阻大物实验数据处理(C++实现)
  18. 公钥,私钥,数字签名,证书图解(转)
  19. 侍魂微信新服务器2019,侍魂胧月传说手游2019年5月17日微信问答试炼答案
  20. 想学板绘,需要练习线稿么,线稿怎么画好看点?

热门文章

  1. 景甜种甜、雨昕助攻,聚划算垂类借势攻擂
  2. 字符串strip()介绍
  3. js 特效 手风琴效果
  4. C语言停车场管理系统,使用栈和队列实现
  5. 我的扫地机器人让我了解机器人过程自动化(RPA)
  6. linux 创建子进程,linux中fork同时创建多个子进程的方法(一)
  7. 惠普服务器cpu芯片,惠普、戴尔推出ARM服务器_Intel服务器CPU_服务器产业-中关村在线...
  8. 机器学习中的数学(七):独立成分分析(ICA)以及FastICA算法
  9. C++ operator=
  10. IntelliJ IDEA Working directory设置