NSTimer 引发的循环引用(内存泄漏)| NSTimer强引用

news2024/11/15 5:26:25

在iOS中使用NSTimer(定时器)不当会引发内存泄漏.

NSTimer 有多种创建方式,具体可以看这位朋友的文章:https://blog.51cto.com/u_16099225/6716123

我这里主要讲使用NSTimer 会引发的内存泄漏情况以及解决方法:

内存泄漏出现的场景:

VC A push 到VC B, VC B里启动了一个 NSTimer, 然后VC B push 到VC C (或者 pop 回VC A), 此时 VC B里的 NSTimer 仍在执行(这是不对的),造成的原因就是 VC B里的 NSTimer 没有被及时释放(销毁)掉,我们称之为内存泄漏.

常见的有以下创建方式:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];

    __weak typeof(self) weakSelf = self;
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];
}

- (void)timerClick
{
    NSLog(@"%s",__FUNCTION__);
}

- (void)dealloc
{
    NSLog(@"走了 dealloc方法");
    NSLog(@"%s",__FUNCTION__);
    [self.timer invalidate];
}

不管是 :self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];

还是:NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];

这两种创建的方式 都有一个 target: self, 意思是timer 强引用了当前的self (VC), 那么创建的时候当前的self (VC)又强引用了 timer,你引用我,我引用你,这就造成了循环引用,所以VC B push (或者pop)的时候, arc内存管理会判断我们还需要这个内存(PS:arc内存管理的模式是,自动销毁我们不需要的内存). 就没有帮我们销毁.

所以我们发现 VC B 退出后没有走 - (void)dealloc 方法,timer也就没有被销毁.

解决方法:

一般解决self强引用的问题,会使用弱引用 weakSelf 来代替 self,但是这里不行,因为weakSelf 是用在block 中的.所以解决方法有两种:

方法一:

一是给NSTimer 和 VCB之间加一个“中间人”

原先是互相循环引用:

加个“中间人”

1.1.新建一个继承于NSObject 的类 MiddleObject ,在 MiddleObject 里用 weak 指针定义一个属性target,再构造一个初始化的类方法.

MiddleObject.h 文件:

​​​​​​​MiddleObject.m 文件:

1.2.在VC B 中倒入 #import "MiddleObject.h". 

将target:self 中的 self 替换成 中间类 MiddleObject 

截图如下: 

代码如下:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];


    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[MiddleObject callWithTarget:self] selector:@selector(timerClick) userInfo:nil repeats:YES];
}

这样字就完成了

NSTimer 引用 MiddleObject,

MiddleObject 弱引用VC B

VC B 引用 NSTimer , 

三种关系,解决了两两循环引用的问题

方法二:

方法一有点麻烦,一般我们直接更换一种创建方法: 不使用 有 target的 方法:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];

    __weak typeof(self) weakSelf = self;
    
    //不使用target
       self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
           [weakSelf  timerClick];
       }];
}

- (void)timerClick
{
    NSLog(@"%s",__FUNCTION__);
}

- (void)dealloc
{
    NSLog(@"走了 dealloc方法");
    NSLog(@"%s",__FUNCTION__);
    [self.timer invalidate];
}

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

           [weakSelf  timerClick];

       }];

这种方法,没有target,本身不会造成循环引用, 且在block里也是有了 弱引用weakSelf开启定时器,所以 VC B 销毁的时候,timer 也会走- (void)dealloc方法,销毁掉timer .[self.timer invalidate];

方法三:自己手动销毁timer

虽然说现在都是arc自动销毁,但是我们也可以手动销毁,直接一点. 没有这么多华丽胡哨的.

创建方法还是有target的那种:

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];

在VC B 的 - (void)viewWillDisappear:(BOOL)animated 方法里,直接手动销毁timer

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    
    NSLog(@"%s",__FUNCTION__);
    
    [self.timer invalidate];
}

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

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

相关文章

Java基础之方法与数组

方法 在Java中, 方法的定义包括方法的修饰符、返回类型、方法名、参数列表和方法体。方法既能够模块化的组织代码(当代码规模比较复杂的时候)。也做到代码被重复使用(一份代码可以在多个位置使用)。Java中的方法类似与C语言中的函数&#xf…

Java SpringBoot实战教程:如何一步步构建保险业务管理与数据分析系统

✍✍计算机毕业编程指导师 ⭐⭐个人介绍:自己非常喜欢研究技术问题!专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目:有源码或者技术上的问题欢迎在评论区一起讨论交流! ⚡⚡ Java、…

LlamaIndex 实现 RAG(三)- 向量数据

RAG 中使用向量存储知识和文档数据,召回时通过语意进行搜索。文档转为向量是个非常消耗时的操作,不同 Embedding Model 参数不同,结果维度也不同,消耗的算力也不同。所以通常的做法都会在索引阶段(Embedding&#xff0…

deeplab3-plus(中文翻译)

** Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation 文章目录 Encoder-Decoder with Atrous Separable Convolution for Semantic Image Segmentation1 Introduction2 Related Work3 Methods3.1 Encoder-Decoder with Atrous Convolution…

鸿蒙南向开发:测试框架xdevice核心组件

简介 xdevice是OpenHarmony中为测试框架的核心组件,提供用例执行所依赖的相关服务。 xdevice主要包括以下几个主要模块: command,用户与测试平台命令行交互模块,提供用户输入命令解析,命令处理。config,…

electron仿微信,高度还原,入门项目

效果展示 Electron仿写微信-效果展示 目前完成了一些基础的功能,还在持续开发中,后期会整理开源。 有些样式没有追求百分百还原,只是通过该项目,让大家了解一下Electron创建桌面应用,各种窗口的创建及销毁、事件传递机…

NLP从零开始------13.文本中阶序列处理之语言模型(1)

语言模型( language model) 用于计算一个文字序列的概率, 评估该序列作为一段文本出现在通用或者特定场景中的可能性。每个人的语言能力蕴涵了一个语言模型,当我们说出或写下一段话的时候,已经在不自觉地应用语言模型来帮助我们决定这段话中的…

viscode 自定义片段,快速生成自己的开发模板

设置 ---> 代码片段 2.选择新建全局代码片段文件 3.根据示例自定义配置代码片段 4.示例:vue prefix:内容--> 代表用于触发代码片段的内容 $1, $2 用于制表位,如 $1 代表生成后第一个输入的位置,$2代表第二个,不用自己移动鼠标 {// Place your snippets f…

Sac格式

本文章只作为自己学习时的用法,不通用,大家可不用参考。 sac格式 0.01000000 -1.569280 1.520640 -12345.00 -12345.009.459999 19.45000 -41.43000 10.46400 -12345.00-12345.00 -12345.00 -12…

SQL注入漏洞的基础知识

目录 SQL注入漏洞的定义和原理 SQL注入的类型和攻击方法 SQL注入的防御措施 示例代码 深入研究 SQL注入漏洞的常见攻击场景有哪些? 如何有效防范SQL注入攻击? SQL注入与跨站脚本攻击(XSS)之间有什么区别? 主要…

每日一练【最大连续1的个数 III】

一、题目描述 给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。 二、题目解析 本题同样是利用滑动窗口的解法。 首先进入窗口,如果是1,就直接让right,但是如果是…

【软考】树、森林和二叉树之间的相互转换

目录 1. 说明2. 树、森林转换为二叉树2.1 树转成二叉树2.1 森林转成二叉树 3. 二叉树转换为树和森林 1. 说明 1.树、森林和二叉树之间可以互相进行转换,即任何一个森林或一棵树可以对应表示为一棵叉树,而任何一棵二叉树也能对应到一个森林或一棵树上。 …

SSRF复现

目录 环境 分析测试 写入shell 环境 web-ssrfme docker环境 拉取运行 分析测试 进入网站会显示源码 可以看到过滤了file,dict等,但get传参info会执行phpinfo() 可以发现这里网站ip是172.18.0.3,可以使用这个地址绕过waf 测试看是否存在…

如何实现一次搭建 多平台适配的小程序

如何实现一次搭建 多平台适配的小程序 什么是小程序小程序的优势有什么?如果构建小程序,会用在什么领域和场景?如何实现一站式开发多平台的小程序?你希望了解小程序上哪些功能模块的集成能力? 随着微信、支付宝、百度、…

七、Centos安装LDAP--Docker版--已失败

参考博客: docker 安装 OpenLDAP 及 LdapAdmin桌面版、页面版(osixia/openldap)_docker安装ldap-CSDN博客 LDAP使用docker安装部署与使用_memberof ldap docker-CSDN博客 目录 一、安装Docker Docker基本使用 管理镜像 二、拉取LDAP镜像 配置docker国内的镜像…

Java 入门指南:初识 Java 异常(Exception)

初识Java异常 何为异常 在Java中,异常是一个不需要的或意外的事件,它发生在程序执行期间,即在运行时,破坏了程序指令的正常流程。异常可以被程序捕获(catch)和处理(handle)。 方法…

C/C++逆向:寻找main函数(Debug-x86)

在程序的逆向分析中,寻找main函数在逆向分析中是非常重要的,它是程序的核心执行点,从这里开始,程序的主要逻辑开始展开;在这边我们需要明确两个概念:用户入口(User Entry Point) 和 …

【C语言进阶】深入C语言指针:基础到进阶的跨越

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C语言 “ 登神长阶 ” 🤡往期回顾🤡:C语言数据在内存中的存储 🌹🌹期待您的关注 🌹🌹 ❀C语言指针进阶 &a…

Linux中安装java和tomcat(保姆级教程)

java 篇 JDK是用于开发Java应用程序的软件开发工具包。它包含了编译器、调试器、运行时环境和其他一些开发工具,可以帮助开发人员创建、编译、调试和部署Java应用程序。JDK提供了Java编程语言的开发工具和运行时库,使开发人员能够编写和执行Java代码。 …

TypeScript教程(一)之我们为什么要学TypeScript

根据软件开发设计公司 The Software House 针对 2022 年前端市场状态的调查显示,84% 的受访者都在使用 TypeScript,43% 的受访者甚至认为 TypeScript 将超越 JavaScript 成为前端开发的主要语言。TypeScript 这些年越来越火,可以说是前端工程…