block(六)循环引用-b
在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为,某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身,简单说就是self.theBlock = ^(void){[self dosomething];或者self.otherVar = XXX;或者_otherVar = ...};block的这种循环引用会被编译器捕捉到并及时提醒,那要如何避免呢,我在这里做了个demo测试。
直接看demo
#import "ViewController.h"
#define CJSUPARC 1
typedef void(^myBlock)(void);
  @interface ViewController ()
  @property (nonatomic, copy)myBlock theBlock;
  @end
  @implementation ViewController
  - (void)dealloc
  {
  #if CJSUPARC
      [super dealloc];
  #else
  #endif
      NSLog(@"ViewController dealloc");
  }
  - (void)viewDidLoad {
      [super viewDidLoad];
  #if CJSUPARC
      __block typeof(self) wSelf = self;
      NSLog(@"%@",@(self.retainCount));
      myBlock block = ^(void){
          NSLog(@"%@",@(wSelf.retainCount));
          NSLog(@"%@.block init",[wSelf.class description]);
      };
      self.theBlock = [[block copy] autorelease];
      if (self.theBlock) {
          self.theBlock();
      }
  #else
  //    __block typeof(self) wSelf = self;
  //    __unsafe_unretained typeof(self) wSelf = self;
      __weak typeof(self) wSelf = self;
      myBlock block = ^(void){
          NSLog(@"%@.block init",[wSelf.class description]);
      };
      self.theBlock = [block copy];
      if (self.theBlock) {
          self.theBlock();
      }
  #endif
  }
  @end
MRC模式下运行
2016-01-09 23:50:33.993 CJBlockDemo[52536:2193676] self.retainCount = 6
2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] self.retainCount = 6
2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
2016-01-09 23:50:40.437 CJBlockDemo[52536:2193676] ViewController dealloc
可以看到使用__block能够避免引起循环引用的问题ARC模式下运行
使用__block
  2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
 虽然编译器警告是没有了,但ViewController却没有执行dealloc函数,说明在arc中__block还是会引起retain。
使用__unsafe_unretained
使用__weak
  2016-01-09 23:50:33.994 CJBlockDemo[52536:2193676] ViewController.block init
  2016-01-09 23:50:40.437 CJBlockDemo[52536:2193676] ViewController dealloc
都可以避免循环引用的问题,但由于前者是unsafe的,会造成野指针问题,所以尽量少用unsafe_unretained关键字
另外在多线程环境下(block中的wSelf有可能被析构的情况下),需要先将self转为strong指针,避免在运行到某个关键步骤时self对象被析构。
可参考AFNetworking代码:
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback =  ^(AFNetworkReachabilityStatus status) {
    __strong __typeof(weakSelf)strongSelf = weakSelf;
    strongSelf.networkReachabilityStatus = status;
    if (strongSelf.networkReachabilityStatusBlock) {
         strongSelf.networkReachabilityStatusBlock(status);
    }
};
- 第一行:__weak __typeof(self)weakSelf = self;
为防止callback内部对self强引用,weak一下。
其中用到了__typeof(self),这里涉及几个知识点: 
- __typeof、__typeof__、typeof的区别
恩~~他们没有区别,但是这牵扯一段往事,在早期C语言中没有typeof这个关键字,__typeof、__typeof__是在C语言的扩展关键字的时候出现的。
typeof是现代GNU C++的关键字,从Objective-C的根源说,他其实来自于C语言,所以AFNetworking使用了继承自C的关键字。 - 对于老的LLVM编译器上面这句话会编译报错,所以在很早的ARC使用者中流行__typeof(&*self)这种写法,原因如下
大致说法是老LLVM编译器会将__typeof转义为 XXX类名 *const __strong的__strong和前面的__weak关键字对指针的修饰又冲突了,所以加上&*对指针的修饰。 
第三行:__strong __typeof(weakSelf)strongSelf = weakSelf;
self转回strong了,这里__typeof()里面写的是weakSelf,里面写self也没有问题,因为typeof是编译时确定变量类型,所以这里写self 不会被循环引用。第四、五、六行,如果不转成strongSelf而使用weakSelf,后面几句话中,有可能在第四句执行之后self的对象可能被析构掉,然后后面的StausBlock没有执行,导致逻辑错误。
最后第五行,使用前对block判空。
原文链接:https://www.jianshu.com/p/a87ed8a4363a =========================看法分割线===============
是否所有的Block中,使用self 都会导致循环引用?

系统自带Block不会发生循环引用
如图,使用系统自带的UIView 的Blcok,控制器能被销毁-->说明没有发送循环引用。
原理: UIView的调用的是类方法,当前控制器不可能强引用一个类 ,所以循环无法形成 --> 动画block不会造成循环引用的原因。
所以通过实践得出第一个结论--> 并不是所有的Block中使用self,都会导致循环引用!
问题二:面试官问:那除了系统自带的方法中的Block,你在其他Block中使用self 会导致循环引用吗? -->可答:AFN框架!
最常用的数据请求框架-- AFNetWorking框架的Block是否会强引用?

AFN的Block是否会导致循环引用测试
如上图所示,在AFN的 block { xxx self.view } 使用self,并不会导致循环引用!
原理:AFN无循环是因为绝大部分情况下,你的网络类对象是不会被当前控制器引用的,这时就不会形成引用环。(查阅资料得知)
小tips:也可能AFN底层有自己做了操作,这里没探究到AFN框架底层,仅知道AFN不会造成循环引用。
那什么情况下会导致循环引用呢? --> 自定义Block

自定义Block中使用self
添加 viewDidLoad 提示框-->每次进入都打印viewDidLoad,可以确定是否离开视图控制器-->如果是,但是没有调用dealloc --> 循环引用

循环引用
这时候,我们发现循环引用发生了!所有我们答道:“我们在实际开发中,使用自定义Block,在Block { xxx }中使用self,导致了循环引用 ”
循环引用导致的原因: 相互强指向

循环引用原因
如何解决-->使用weakSelf,这个解决方法估计没见过一百次的,都不算是真正参加过iOS面试的。
----------------------------- 华丽分割线--------------------------------------
一个大写的excuse me 写脸上,49行都报警告了,而且提示可能发送循环引用,这你都能因为这样导致循环引用??这面试官如果知道这个,应该不会这么友好的放过你吧?
由于现在学iOS的太多了,所有可能面试官如果对于循环引用比较了解的话,并不会因为我们回答了上面两个问题就放过我们~他可能会接着问:那如果是我们自己写的Block,(非系统和AFN),在Block中使用self,是否一定会发生循环引用~
探究四:自定义Block是否一定会发生循环引用?

在其他控制器声明一个强指向的Block

调用Blcok

执行效果
如图:发现oneVC被销毁了,说明,自己定义的Block,里面使用了self,并不一定会发生循环引用!
原理:block --> 强指向了self,但是self,并没有指向Block!-->并没有一个 self.block 或者 成员变量 @property block ,所有Block并没有被强指向-->没有发送循环引用!
-->Tips:循环引用发生的条件就是持有这个block的对象,被block里边加入的对象持有。
逼格出现了!!华丽分割线! 既然系统的Block、AFN、都不会发生循环引用,自定义Block又有这么明显的提示-->实际开发中不会遇到循环引用??
---------------------------------高逼格分割线-----------------------------------------
实际开发中:使用通知(NSNotifation),调用系统自带的Block,在Block中使用self --> 会发生循环引用。

通知的接收方法
现在iOS的通知已经比较好用了,如图第二个方法,我最常用的,特别方便,不需要写@selector(方法)+ 调用,直接写在Block中,就可以实现接收通知之后实现的代码。

twoVC发送通知 --> 给oneVC

oneVC 接收通知

使用通知-发生循环引用
如图!这才是实际开发中-->真正有可能发生循环引用的地方!确实也是在通知的Block,但是这次的循环引用并没有提示,而且也确实发生了 --> 这才是真正告诉面试官:我们做过有实际开发,并且是在真实的开发环境中遇到了-->真正的循环引用!!
解决办法-->weakSelf!
参考链接 http://www.jb51.net/article/108171.htm
block(六)循环引用-b的更多相关文章
- 避免Block的循环引用
		
避免Block的循环引用 什么是循环引用,什么时候发生循环引用 1 循环引用就是当self 拥有一个block的时候,在block 又调用self的方法.形成你中有我,我中有你,谁都无法将谁释放的困局 ...
 - 关于block的循环引用的问题
		
在block的循环引用的问题上我们都知道如果在block内部修改外部的变量的时候,要加__block以防止循环引用的问题,但是如果block是当前对象的一个属性的时候,要修改当前对象的一个属性的时候就 ...
 - iOS开发Block的介绍以及Block的循环引用问题
		
1:block的循环引用问题最主要记住两点: 如果[block内部]使用[外部声明的强引用]访问[对象A], 那么[block内部]会自动产生一个[强引用]指向[对象A] 如果[block内部]使用[ ...
 - iOS - Block的循环引用内存泄漏问题探索
		
循环引用的原因 众所周知,ARC下用block会产生循环引用的问题,造成泄露的原因是啥呢? 最简单的例子,如下面代码: [self.teacher requestData:^(NSData *data ...
 - block的循环引用
		
什么是循环引用呢? 就是我引用你,你引用我,就会造成循环引用,双方都不会被销毁,导致内存泄漏. _block = ^{ NSLog(@"%@", self); }; block会 ...
 - block之---循环引用
		
block内部引用外界对象的原则:block会对他内部所有的强指针进行强引用. 验证原理: 在主控制器中modal出ModalVC控制器,ModalVC中有强引用的block属性,在block内部使用 ...
 - weakSelf 运用 strongSelf来解决block的循环引用
		
SDWebImage 中有一段源码: #if SD_UIKIT Class UIApplicationClass = NSClassFromString(@"UIApplication&qu ...
 - 避免在block中循环引用(Retain Cycle in Block)
		
让我们长话短说.请参阅如下代码: - (IBAction)didTapUploadButton:(id)sender { NSString *clientID = @"YOUR_CLIENT ...
 - iOS开发小技巧--键盘处理以及解决block造成循环引用的小技巧
 - block使用小结、在arc中使用block、如何防止循环引用
		
引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题: [小测试]你真的知道blocks在Objective-C中是怎么工作的吗 ...
 
随机推荐
- mysql生成不重复随机数(unique number generation)
			
转自:http://blog.csdn.net/dreamer2020/article/details/52049629 问题来源 业务中有时会遇到要生成不重复随机数的情况,例如,新生成一个商品编号. ...
 - mysql存储过程,游标实例
			
CREATE DEFINER=`root`@`%` PROCEDURE `vir`.`task_payment_byonlinedown`()begin declare _mobile varchar ...
 - Android Studio Gradle优化方法
			
第一步:打开AS安装所在的位置,用记事本打开"红色框"选中的文件. 如图: 第二步:打开"studio64.exe.vmoptions"文件后修改里面的值,修改 ...
 - zeroclipboard实现多浏览器复制到粘贴板功能
			
zeroclipboard实现多浏览器复制到粘贴板功能(单个复制按钮和多个复制按钮) 为了更好的用户体验,现在很多网站中文本框的内容只需要点击复制按钮这样就能把内容复制到粘贴板了:出于兼容性的考虑,基 ...
 - 微信小程序 - 3d轮播图组件(基础)
			
<!-- 目前仅支持data数据源来自banner,请看测试案例 ################ 以上三种形式轮播: 1. basic 2. 3d 3. book basic即普通轮播 3d即 ...
 - 微信小程序 - setData:key的几种用法
			
1. 常量key渲染 2. 变量key渲染(字符串和变量先拼接) 3.对象key渲染
 - 方法(method)和函数(function)有什么区别?
			
方法(method)和函数(function)有什么区别? 定义和参数区别 函数是独立的功能,与对象无关,需要显示的传递数据 方法与对象和类相关,依赖对象而调用,可以直接处理对象上的数据,也就是隐式传 ...
 - SSH使用Log4j
			
1. 将Jar文件log4j-1.2.14.jar导入项目. 2. 在src文件夹下新建log4j.properties文件: log4j.rootLogger = debug,stdout,D,E ...
 - Spring Cloud Zuul的一个坑
			
Spring Cloud 版本: Dalston.SR5 今天使用Zuul发现一个和动态刷新相关的问题,动态刷新使用的是 /bus/refresh,即我的Zuul连着一个Rabbitmq,我这里是使用 ...
 - Ubuntu下的init.d管理update-rc.d
			
计算机在启动的时候会自动执行一些脚本,用于启动一些应用程序服务,update-rc.d 是管理这些脚本的常用命令之一. 首先这是 LinuxQuestions 中对 update-rc.d 的定义: ...