【iOS】——Block循环引用

news2024/11/18 16:51:23

循环引用原因

如果在Block中使用附有_ _strong修饰符的对象类型自动变量,那么当Block从栈复制到堆时,该对象为Block所持有,这样容易引起循环引用。

HPPerson *person = [[HPPerson alloc] init];
 person.block = ^{
             NSLog(@"person.age--- %d",person.age);
 };

在上面代码中,person对象强持有block对象,在block语法中,block对象又强持有person对象,此时达成互相强持有,谁也无法释法谁,造成循环引用。

在这里插入图片描述

当造成block循环引用时编译器会检测出并发出警告

在这里插入图片描述

另外,如果block内没有使用self也会捕获self,引起循环引用

typedef void (^blk_t) (void);

@interface HPPerson : NSObject
{
    blk_t _block;
    int _age;
}
@end
#import "HPPerson.h"

@implementation HPPerson
-(id)init {
    self = [super init];
    _block = ^{NSLog(@"age = %d", _age);};
    return self;
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
}
@end

在这里插入图片描述

这是因为虽然没有使用self,但使用了self对象中的结构体成员,因此也会捕获self。

避免循环引用

使用weak修饰符

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        HPPerson *person = [[HPPerson alloc] init];
        person.age = 10;
        
        __weak HPPerson *weakPerson = person;
        
        person.block = ^{
            NSLog(@"person.age--- %d",weakPerson.age);
        };
        NSLog(@"--------");

    }
    return 0;
}

编译完成之后是

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
    // block内部对weakPerson是弱引用
  HPPerson *__weak weakPerson;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

局部变量消失时候,对于HPPerson来说,只有一个弱指针指向它,那它就销毁,然后block也销毁。在这里插入图片描述

使用__unsafe_unretained修饰符

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        HPPerson *person = [[HPPerson alloc] init];
        person.age = 10;
        
        __unsafe_unretained HPPerson *weakPerson = person;
        
        person.block = ^{
            NSLog(@"person.age--- %d",weakPerson.age);
        };
        NSLog(@"--------");

    }
    return 0;
}

编译完成之后是

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  HPPerson *__unsafe_unretained weakPerson;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, HPPerson *__unsafe_unretained _weakPerson, int flags=0) : weakPerson(_weakPerson) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

虽然__unsafe_unretained可以解决循环引用,但是最好不要用,因为:

  • __weak:不会产生强引用,指向的对象销毁时,会自动让指针置为nil
  • __unsafe_unretained:不会产生强引用,不安全,指向的对象销毁时,指针存储的地址值不变,会造成野指针

使用_ _Block修饰符

int main(int argc, const char * argv[]) {
    @autoreleasepool {
       __block HPPerson *person = [[HPPerson alloc] init];
        person.age = 10;
        person.block = ^{
            NSLog(@"person.age--- %d",person.age);
            //这一句不能少
            person = nil;
        };
        // 必须调用一次
        person.block();
        NSLog(@"--------");
    }
    return 0;
}

使用_ _Block修饰符解决循环引用时,需要注意的点有:

  • 在block对象中需要将_ _block变量置为nil
  • 必须调用block对象

如果不调用block对象时,会造成下面情况的循环引用:

  1. HPPerosn类对象持有Block
  2. Block持有_ _block变量
  3. _ _block变量持有HPPerson对象

在这里插入图片描述

因为block会对__block产生强引用

__block HPPerson *person = [[HPPerson alloc] init];
person.block = ^{
        NSLog(@"person.age--- %d",person.age);
        //这一句不能少
        person = nil;
};

person对象本身就对block是强引用

@property (copy, nonatomic) HPBlock block;

__block对person产生强引用

struct __Block_byref_person_0 {
  void *__isa;
__Block_byref_person_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
    //`__block`对person产生强引用
 HPPerson *__strong person;
};

当执行完person = nil时候,__block解除对person的引用,进而,全都解除释放了。 但是必须调用person = nil才可以,否则,不能解除循环引用

在这里插入图片描述

强弱共舞

  • 当Block捕获self时,应该使用弱引用,这样即使Block持有self的引用,也不会阻止self被释放。
  • 由于弱引用可能变成nil,因此在Block内部使用self之前,需要检查它是否为nil
  • 为了避免在Block内部因selfnil而导致的崩溃,可以在Block的开始处使用强引用
  • 使用完成之后当Block的作用域结束之后即可释放
#import <UIKit/UIKit.h>
typedef void(^blk_t)(void);
@interface ViewController : UIViewController
@property (nonatomic, strong) blk_t block;
@property (nonatomic, copy) NSString *name;

@end
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.name = @"Hello";
    __weak typeof(self) weakSelf = self;
    self.block = ^(){
         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", strongWeak.name);

    };

    self.block();
}


@end

此时self持有block,block弱引用self,弱引用会自动变为nil,强持有中断,所以不会引起循环引用。但该方法可能存在中途就释放掉的问题(手动延迟,可能需要调用self.name的时候name已经被释放了)如果self被销毁,那么block则无法获取name。

因此可以改进上面的代码:

    self.name = @"Hello";

    __weak typeof(self) weakSelf = self;
    self.block = ^(){
        __strong __typeof(weakSelf)strongWeak = weakSelf;

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", strongWeak.name);
        });
    };
    self.block();

在完成block中的操作之后,才调用了dealloc方法。添加strongWeak之后,持有关系为:self -> block -> strongWeak -> weakSelf -> self。

weakSelf被强引用了就不会自动释放,因为strongWeak只是一个临时变量,它的声明周期只在block内部,block执行完毕后,strongWeak就会释放,而弱引用weakSelf也会自动释放。

参数形式解决循环引用

通过给block传参(指针拷贝)

    // 循环引用
    self.name = @"Hello";
    self.block = ^(ViewController * ctrl){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", ctrl.name);
        });
    };
    self.block(self);

Block循环引用场景

    // staticSelf_定义:
    static ViewController *staticSelf_;

    - (void)blockWeak_static {
        __weak typeof(self) weakSelf = self;
        staticSelf_ = weakSelf;
    }

weakSelf虽然是弱引用,但是staticSelf_静态变量,并对weakSelf进行了持有,staticSelf_释放不掉,所以weakSelf也释放不掉!导致循环引用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1949099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis使用场景-热点数据缓存

什么是缓存&#xff1f; 为了把一些经常访问的数据放入缓存中已减少对数据库的访问&#xff0c;从而减少数据库的压力&#xff0c;提高程序的性能。【内存中存储】-效率快 缓存的原理 什么样的数据适合放入缓存中&#xff1f; 1.查询频率高且修改频率低 2.数据安全性低 哪些组件…

《python语言程序设计》第6章第7题财务应用程序:计算未来投资,编写函数计算制定年数以给定利率

记住这里增加循环应该是以年为单位。但是添加的数是月为单位 此处需留意其实点不是1&#xff0c;1代表1年&#xff0c;这里月所以其实是12&#xff0c;这里的单位是月&#xff0c;而不是年。 python for i in range(12,monthNum12,12) 如果你把12都换成1呢&#xff1f;&…

本地生活抽佣系统搭建:如何让系统具有竞争优势?

随着本地生活的潜力不断展现&#xff0c;本地生活服务商逐渐成为新兴职业中的一大热门&#xff0c;本地生活抽佣系统搭建的热度也一直保持着飙升的状态。 抖音生活发布的《2023年数据报告》显示&#xff0c;2023年&#xff0c;抖音生活服务平台总交易额增长256%&#xff0c;抖…

监测Nginx访问日志状态码,并做相应动作

文章目录 引言I 监测 Nginx 访问日志情况,并做相应动作1.1 前提准备1.2 访问日志 502 情况,重启 bttomcat9服务1.3 其他案例:访问日志 502 情况,重启 php-fpm 服务II 将Shell 脚本check499.sh包装成systemd服务2.1 创建systemd服务2.2 配置service2.3 开机启动2.4 其他常用…

自监督学习概述(Self-Supervised Learning,SSL)

自监督学习&#xff08;Self-Supervised Learning&#xff0c;SSL&#xff09;是一种机器学习方法&#xff0c;旨在利用未标记数据进行训练。这种方法通过从数据本身生成伪标签&#xff0c;来创建监督信号&#xff0c;使得模型能够学习有效的数据表示。自监督学习在深度学习领域…

HTTP传输下载和P2P传输下载的区别?

HTTP传输下载和P2P&#xff08;Peer-to-Peer&#xff09;传输下载在多个方面存在显著的区别&#xff0c;以下是详细的分析&#xff1a; 1. 工作原理 HTTP传输下载&#xff1a; HTTP&#xff08;Hypertext Transfer Protocol&#xff09;是一种用于在Web上进行数据通信的协议&…

PHP多功能投票系统源码小程序

&#x1f389;决策不再难&#xff01;「多功能投票小程序」一键搞定所有选择困难症✨ &#x1f914;选择困难&#xff1f;「多功能投票小程序」来救场&#xff01; 每次聚会、团队讨论还是日常小决策&#xff0c;是不是总有那么几个瞬间让你陷入“选哪个好呢&#xff1f;”的…

spine to unity-2.利用边缘框实现实时碰撞检测

主要讲spine的边缘框&#xff0c;在unity中&#xff0c;实现实时碰撞检测。其中使用的素材&#xff0c;是我为独立游戏ink制作的动画。独立游戏ink的开发日志&#xff0c;在小红薯持续更新中。spine工具包的安装&#xff0c;下载请参考spine to unity-1spine BoundingBoxFollow…

【MySQL篇】Percona XtraBackup标准化全库完整备份策略(第三篇,总共五篇)

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长Oracle、MySQL、SQLserver、阿里云AnalyticDB for MySQL(分布式数据仓库)、Linux&#xff0c;也在扩展大数据方向的知识面✌️…

STM32H7的LPUART基础和唤醒示例

STM32H7的LPUART基础知识 硬件框图低功耗的高级特性低功耗串口的时钟以及波特率低功耗串口发送时序低功耗串口支持的唤醒方式 LPUART 的全称是 Low power universal synchronous asynchronous receiver transmitter&#xff0c;中文意思是低功耗通用异步收发器&#xff0c;简称…

【C语言】栈的实现(数据结构)

前言&#xff1a; 还是举一个生活中的例子&#xff0c;大家都玩过积木&#xff0c;当我们把积木叠起来的时候&#xff0c;如果要拿到最底部的积木&#xff0c;我们必须从顶端一个一个打出&#xff0c;最后才能拿到底部的积木&#xff0c;也就是后进先出&#xff08;先进后出&a…

Python - 开源库 ReportLab 库合并 CVS 和图像生成 PDF 文档

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/140281680 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 Report…

[Spring] MyBatis操作数据库(基础)

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏: &#x1f9ca; Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 &#x1f355; Collection与…

Elasticsearch概念及ELK安装

1、Elasticsearch是什么 它是elastic技术栈中的一部分。完整的技术栈包括&#xff1a; Elasticsearch&#xff1a;用于数据存储、计算和搜索 Logstash/Beats&#xff1a;用于数据收集 Kibana&#xff1a;用于数据可视化 整套技术栈被称为ELK&#xff0c;经常用来做日志收集…

WPF启动失败报System.Windows.Automation.Peers.AutomationPeer.Initialize()错误解决

问题描述 win10系统上WPF程序启动后就崩溃&#xff0c;通过查看崩溃日志如下&#xff1a; 应用程序: xxx.exe Framework 版本: v4.0.30319 说明: 由于未经处理的异常&#xff0c;进程终止。 异常信息: System.TypeLoadException 在 System.Windows.Automation.Peers.Automatio…

CMake 使用 OpenCV:从库中查找包含头文件

前言 在开发使用 OpenCV 的项目时&#xff0c;正确配置 CMake 是确保项目顺利构建和运行的关键。开发过程经常存在各种各样的意外和偶然, 是困难也是收获. 比如一直好好的项目, include某个头文件, 编译突然出现:No such file or directory CmakeTest/test_opencv.h:4: error:…

一套成熟的实验室信息管理系统源码,.Net 检验系统LIS源码,实现从采集、检测、报告、归档的全程跟踪管理

一套成熟的实验室信息管理系统源码。在长期的医疗信息化实践中&#xff0c;我们分析总结了大量客户实例&#xff0c;建立了以病人为中心、以业务处理为基础、以提高检验科室管理水平和工作效率为目标的产品开发思路&#xff0c;将医学检验、科室管理和财务统计等检验科室/实验室…

ControlNet on Stable Diffusion

ControlNet on Stable Diffusion 笔记来源&#xff1a; 1.Adding Conditional Control to Text-to-Image Diffusion Models 2.How to Use OpenPose & ControlNet in Stable Diffusion 3.ControlNet与DreamBooth&#xff1a;生成模型的精细控制与主体保持 4.Introduction t…

【Python实战】Google Chrome的离线小恐龙游戏

文章目录 Google Chrome的离线小恐龙游戏项目结构大纲 &#x1f4ca;&#x1f463;逐步编码过程 &#x1f9e9;&#x1f4a1;第一步&#xff1a;项目初始化与主程序框架第二步&#xff1a;实现T-Rex的跳跃功能第三步&#xff1a;添加障碍物和碰撞检测第四步&#xff1a;添加得分…

Python3网络爬虫开发实战(1)爬虫基础

一、URL 基础 URL也就是网络资源地址&#xff0c;其满足如下格式规范 scheme://[username:password]hostname[:port][/path][;parameters][?query][#fragment] scheme&#xff1a;协议&#xff0c;常用的协议有 Http&#xff0c;https&#xff0c;ftp等等&#xff1b;usern…