Block循环引用

什么情况下block会造成循环引用

ARC 情况下 block为了保证代码块内部对象不被提前释放,会对block中的对象进行强引用,就相当于持有了其中的对象,而如果此时block中的对象又持有了该block,就会造成循环引用。

常见误区

误区一.所有block都会造成循环引用

在block中,并不是所有的block都会循造成环引用,比如UIView动画block、Masonry添加约束block、AFN网络请求回调block等。    

1. UIView动画block不会造成循环引用是因为这是类方法,不可能强引用一个类,所以不会造成循环引用。    

2. Masonry约束block不会造成循环引用是因为self并没有持有block,所以我们使用Masonry的时候不需要担心循环引用。

  • Masonry内部代码
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
//这里并不是self.block (self并没有持有block 所以不会引起循环引用)
block(constraintMaker);
return [constraintMaker install];
}

3.AFN请求回调block不会造成循环引用是因为在内部做了处理。
block先是被AFURLSessionManagerTaskDelegate对象持有。而AFURLSessionManagerTaskDelegate对象被mutableTaskDelegatesKeyedByTaskIdentifier字典持有,在block执行完成后,mutableTaskDelegatesKeyedByTaskIdentifier字典会移除AFURLSessionManagerTaskDelegate对象,这样对象就被释放了,所以不会造成循环引用。

  • AFN内部代码
#pragma mark - 添加代理
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
//block被代理引用
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//设置代理
[self setDelegate:delegate forTask:dataTask]; delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
#pragma mark - 设置代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate); [self.lock lock];
//代理被mutableTaskDelegatesKeyedByTaskIdentifier字典引用
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
#pragma mark - 任务完成
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
if (delegate) {
//任务完成,移除
[self removeDelegateForTask:dataTask];
[self setDelegate:delegate forTask:downloadTask];
} if (self.dataTaskDidBecomeDownloadTask) {
self.dataTaskDidBecomeDownloadTask(session, dataTask, downloadTask);
}
}
#pragma mark - 移除任务代理
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task); AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[self.lock lock];
[delegate cleanUpProgressForTask:task];
[self removeNotificationObserverForTask:task];
//移除
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}
 

误区二.block中只有self会造成循环引用

在block中并不只是self会造成循环引用,用下划线调用属性(如_name)也会出现循环引用,效果和使用self是一样的(内部会用self->name去查找)。

//会造成循环引用
_person1 = [[Person alloc] init];
_person2 = [[Person alloc] init];
_person2.name = @"张三"; [_person1 Block:^{
NSLog(@"%@",_person2.name)
}];

误区三.通过__weak __typeof(self) weakSelf = self;可以解决所有block造成的循环引用

大部分情况下,这样使用是可以解决block循环引用,但是有些情况下这样使用会造成一些问题,比如在block中延迟执行一些代码,在还没有执行的时候,控制器被销毁了,这样控制器中的对象也会被释放,__weak对象就会变成null。所以会输出null。

//在延迟执行期间,控制器被释放了,打印出来的会是**(null)**
_person1 = [[Person alloc] init];
_person2 = [[Person alloc] init];
_person2.name = @"张三";
__weak __typeof(self) weakSelf = self;
[_person1 Block:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakSelf.person2.name);
});
}];
 

误区四.用self调用带有block的方法会引起循环引用

并不是所有通过self调用带有block的方法会引起循环引用,需要看方法内部有没有持有self。

//不会引起循环引用
[self dismissViewControllerAnimated:YES completion:^{
NSLog(@"%@",self.string);
}];
 

如何避免循环引用

方式一、weakSelf、strongSelf结合使用

使用weakSelf结合strongSelf的情况下,能够避免循环引用,也不会造成提前释放导致block内部代码无效。

_person1 = [[Person alloc] init];
_person2 = [[Person alloc] init];
_person2.name = @"张三";
__weak __typeof(self) weakSelf = self;
[_person1 Block:^{
__typeof(&*weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.person2.name);
});
}];
 

方式二、block的外部对象使用week

外部对象通过week修饰,使用全局弱指针指向一个局部强引用对象,这样局部变量在超出其作用域后也不会被销毁,因为是弱指针,所以不会造成循环引用。

@interface CLViewController ()
//弱引用指针
@property (nonatomic,weak) Person *person1;
@property (nonatomic,strong) Person *person2; @end @implementation CLViewController - (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
//局部强引用对象
Person *person1 = [[Person alloc] init];
_person1 = person1; _person2 = [[Person alloc] init];
_person2.name = @"张三";
[_person1 Block:^{
NSLog(@"%@",self.person2.name);
}]; }
 

方式三.将对象置为nil

使用完对象之后就没有必要再保留该对象了,将对象置为nil。在ARC中,被置为nil的对象会被销毁。虽然这样也可以达到破除循环引用的效果,但是这样使用起来很不方便,如果一个对象有多个block,在前面将对象置空,后面的block就不会执行,所以使用这种方法来防止循环引用,需要注意在合适的位置将对象置空。

_person1 = [[Person alloc] init];
_person2 = [[Person alloc] init];
_person2.name = @"张三";
[_person1 Block:^{
NSLog(@"%@",self.person2.name);
//置空,避免循环引用
_person1 = nil;
}];
//由于上面已经将对象置空,所以这里block里边的代码不会执行
[_person1 Block:^{
NSLog(@"%@",self.person2.name);
}];
 

虽然这种方式使用起来不是很友好,但是在封装block的时候,可以考虑使用完马上置空当前使用的block,这样使用的时候就不需要考虑循环引用的问题。

- (void)back
{
if (self.BackBlock)
{
self.BackBlock(button);
}
//使用完,马上置空当前block
self.BackBlock = nil;
}
 

总结

使用block的时候,我们首先需要做的就是判断当前block是否会引起循环引用,如果会引起循环引用,再考虑采取哪种方式来避免循环引用。

iOS-block循环引用详解和应用的更多相关文章

  1. Block循环引用详解

    前言 在项目中经常用到block,使用不当就很容易因为循环引用而造成内存泄漏.本文分析了block循环引用形成原因以及处理办法,如果有什么不对或者疑问请留言. 什么情况下block会造成循环引用 bl ...

  2. ios - block循环引用Demo示例

    当实例变量中有了block属性,并且用copy来修饰,但是当调用block中的代码的时候,如果block中运用了self.属性的时候回造成循环引用. // // ViewController.h // ...

  3. iOS Block循环引用

    在介绍block循环引用前我们先了解一下typeof. typeof是什么??? typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型. 它返回值是一个字符串,该字符串说明运算数的类 ...

  4. IOS block 循环引用的解决

    在介绍block循环引用前我们先了解一下typeof. typeof是什么??? typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型. 它返回值是一个字符串,该字符串说明运算数的类 ...

  5. ios block循环引用问题

    ios开发中,开了ARC模式,系统自动管理内存,如果程序中用到了block就要注意循环引用带来的内存泄露问题了 这几天遇到一个问题,正常页面dismiss的时候是要调用dealloc方法的,但是我的程 ...

  6. iOS中Block循环引用的问题

    说到循环引用问题,想必大家都碰到过吧,比如在使用Block的时候,使用__weakSelf来代替self解决等,但是对于这个,还是有不少可以探索的点,下面我就来说下,希望对大家有所帮助. 是否所有的B ...

  7. ios新特征 ARC详解

    IOS ARC 分类: IOS ARC2013-01-17 09:16 2069人阅读 评论(0) 收藏 举报   目录(?)[+]   关闭工程的ARC(Automatic Reference Co ...

  8. 批处理命令 For循环命令详解!

    批处理for命令详解FOR这条命令基本上都被用来处理文本,但还有其他一些好用的功能!看看他的基本格式(这里我引用的是批处理中的格式,直接在命令行只需要一个%号)FOR 参数 %%变量名 IN (相关文 ...

  9. 【转】批处理命令 For循环命令详解!

    批处理for命令详解FOR这条命令基本上都被用来处理文本,但还有其他一些好用的功能!看看他的基本格式(这里我引用的是批处理中的格式,直接在命令行只需要一个%号)FOR 参数 %%变量名 IN (相关文 ...

随机推荐

  1. consul 多节点/单节点集群搭建

    三节点配置 下载安装包 mkdir /data/consul mkdir /data/consul/data curl -SLO https://github.com/consul/1.9.5/con ...

  2. Spring:Spring的各jar包依赖及作用详解

    spring-core.jar(必须有的核心jar包) 这个jar 文件包含Spring 框架基本的核心工具类.Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己 ...

  3. MySQL字符串操作函数

    使用方法:concat(str1,str2,-)   返回结果为连接参数产生的字符串.如有任何一个参数为NULL ,则返回值为 NULL. mysql> select concat('11',' ...

  4. C语言:特殊点

    编译器给变量分配内存在函数外部定义的变量叫做全局变量(Global Variable),在函数内部定义的变量叫做局部变量(Local Variable)一个变量,即使不给它赋值,它也会有一个默认的值, ...

  5. 创建自己的RSA密钥来保护web.config 加密数据库连接字符串

    通过创建自己的RSA密钥来保护web.config1创建RSA密钥:C:\Windows\Microsoft.NET\Framework64\v4.0.30319>aspnet_regiis - ...

  6. 学生信息管理系统--基于jsp技术和MySQL的简单增删改查

    web实现增删改查的方式有很多啊,对于初学者来说当然是要先了解各部分的传值的方式.本篇博客从jsp技术的最基础方面进行说明. 一.什么是jsp技术 首先,我们要了解什么是jsp技术. jsp技术是基于 ...

  7. Day5 Scanner对象学习.

    Scanner对象 Java.until.Scanner 我们可以通过Scanner类来获取用户的输入. 基本语法: Scanner s = new Scanner (System . in) ; 通 ...

  8. Day3 变量 运算符 及运算符的优先级

    变量 什么是变量: 可以变化的量 Java 是一种强类型语言,每个变量都必须声明其类型. Java变量是程序中最基本的存储单位,其要素包括变量名,变量类型,作用域. 注意事项: 每个变量都有类型, 类 ...

  9. [HAOI2012]外星人 题解

    人类智慧题. 首先,只有 \(\varphi(1)=\varphi(2)=1\).再考虑题目中给的提示: \[\varphi\left(\prod_{i = 1}^m p_i^{q_i}\right) ...

  10. 如何反编译Python写的exe到py

    参考链接: https://blog.csdn.net/qq_44198436/article/details/97314626?depth_1-utm_source=distribute.pc_re ...