「OC」内存管理——定时器内存泄漏

iOS 内存泄漏场景与解决方案 欢迎访问我的博客原文 内存泄漏指的是程序中已动态分配的堆内存(程序员自己管理的空间)由于某些原因未能释放或无法释放,造成系统内存的浪费,导致程序运行速度变慢甚至系统崩溃。 在 iOS 开发中会遇到的内存泄漏场景可以分为几类: 循环引用 当对象 A 强引用对象 B,而对象 B 又强引用对象 A,或者多个对象互相强引用形成一个闭环,这就是循环引用。 Block Block 会对其内部的对象强引用,... 阅读详情

「OC」内存管理——定时器内存泄漏

前言

在我们写项目的过程之中,我们总是会使用NSTimer来帮助我们进行滚动视图的翻页等行为,稍不注意就会导致严重的内存泄漏问题。这篇文章就来简单探究一下如何处理定时器的内存泄漏

定时器的使用

[NSTimer scheduledTimerWithTimeInterval:1.0
                                 target:self   // 关键点:强引用 target
                               selector:@selector(tick)
                               userInfo:nil
                                repeats:YES];

image-20250708192431182

我们如果在控制器之中添加定时器,然后再通过对VC进行dismiss或者pop操作,我们的控制器并不会进行dealloc,这是因为在使用定时器的过程之中出现了循环引用

img

内存泄漏的处理

在dimiss和pop的其他方法之中销毁timer

我们在didMoveToParentViewController添加以下内容

- (void)didMoveToParentViewController:(UIViewController *)parent{
    if (parent == nil) {
       [self.timer invalidate];
        self.timer = nil;
        NSLog(@"timer 走了");
    }
}

我们在离开这个页面之后将计时器置空

image-20250709104315363

中介者模式

在我们使用计时器的时候,其实我们主要是的让计时器能够定时执行对应的方法,对应的target其实没有那么重要,那我们可以使用一个类实现方法,以达到同样的目的,这里我使用runtime的API临时给NSObject添加了一个方法


image-20250709110016254

可以看到我们解决了控制器和定时器的循环引用,但是没有解决对中介者的回收,这种方式还是有一定缺陷的。

我们可以在控制器的dealloc函数里面,将定时器置空,就可以解决这个问题

- (void)dealloc{
    [self.timer invalidate];
    self.timer = nil;
    NSLog(@"%s",__func__);
}

自定义Timer

根据第二个思路,我们可以将timer直接分装为一个类

/*********** JCTimerWapper.m 文件 ***********/
#import "JCTimerWapper.h"
#import <objc/message.h>

@interface JCTimerWapper ()

@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL aSelector;
@property (nonatomic, strong) NSTimer *timer;

@end

@implementation JCTimerWapper

- (instancetype)jc_initWithTimeInterval:(NSTimeInterval)ti 
                                target:(id)aTarget 
                              selector:(SEL)aSelector 
                              userInfo:(nullable id)userInfo 
                               repeats:(BOOL)yesOrNo
{
    if (self = [super init]) {
        // 保存原始目标对象和方法选择器
        self.target = aTarget;
        self.aSelector = aSelector;
        
        // 检查目标对象是否实现了指定的方法
        if (self.target && [self.target respondsToSelector:self.aSelector]) {
            // 获取原始方法的实现细节
            Method method = class_getInstanceMethod([self.target class], aSelector);
            const char *type = method_getTypeEncoding(method);
            
            // 给封装器动态添加相同签名的方法
            class_addMethod([self class], 
                            aSelector, 
                            (IMP)jc_fireHandler, 
                            type);
            
            // 创建定时器,目标设为封装器自身
            self.timer = [NSTimer scheduledTimerWithTimeInterval:ti 
                                                         target:self 
                                                       selector:aSelector 
                                                       userInfo:userInfo 
                                                        repeats:yesOrNo];
        }
    }
    return self;
}

// 动态添加的定时器事件处理方法
void jc_fireHandler(JCTimerWapper *wapper, SEL _cmd, id timerParam) {
    // 1. 检查原始目标是否还存活
    if (wapper.target) {
        // 2. 构造原始objc_msgSend函数指针
        void (*jc_msgSend)(void *, SEL, id) = (void *)objc_msgSend;
        
        // 3. 转发消息到原始目标对象
        jc_msgSend((__bridge void *)(wapper.target), 
                   wapper.aSelector,
                   timerParam);
    } 
    else {
        // 4. 目标对象已释放,清理定时器资源
        [wapper.timer invalidate];
        wapper.timer = nil;
    }
}

// 销毁定时器
- (void)jc_invalidate {
    [self.timer invalidate];
    self.timer = nil;
}

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

@end

封装完之后我们就可以直接进行使用,当我们alloc的时候就自动创建计时器

//定义
self.timerWapper = [[JCTimerWapper alloc] cjl_initWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES];

//释放
- (void)dealloc{
     [self.timerWapper jc_invalidate];
}

使用NSProxy虚拟类

我们还是使用runtime的API

//************NSProxy子类************
@interface JCProxy : NSProxy
+ (instancetype)proxyWithTransformObject:(id)object;
@end

@interface JCProxy()
@property (nonatomic, weak) id object;
@end

@implementation JCProxy
+ (instancetype)proxyWithTransformObject:(id)object{
    JCProxy *proxy = [JCProxy alloc];
    proxy.object = object;
    return proxy;
}
-(id)forwardingTargetForSelector:(SEL)aSelector {
    return self.object;
}

//************解决timer强持有问题************
self.proxy = [JCProxy proxyWithTransformObject:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.proxy selector:@selector(fireHome) userInfo:nil repeats:YES];

//在dealloc中将timer正常释放
- (void)dealloc{
    [self.timer invalidate];
    self.timer = nil;
}
爱奇艺iOS校招笔试全解析:从内存管理到签名上架 iOS开发作为移动互联网的核心方向,对工程师的要求早已超越单纯编码。内存管理、多线程、布局适配等底层机制,决定了App在复杂场景下的稳定性与性能表现。以ARC为例,编译器自动插入引用计数操作,却无法解决循环引用问题;GCD队列虽简化并发编程,但同步派发到串行队列仍可能引发死锁。理解这些原理,不仅有助于写出高质量代码,更是排查线上崩溃、优化用户体验的关键能力。在视频类App中,弱网适配、播放器性能、界面流畅度都直接依赖上述技术功底。爱奇艺2018年校招iOS笔试正是以此为纲,系统考察了语言基础、系统机制、UI 阅读详情

相关推荐

深入解析JS-Bridge:基于WKWebView的OC与JavaScript互调实战

在移动应用开发中,跨语言调用是连接Web动态能力与原生稳定性的核心技术。其本质是通过进程间通信(IPC)机制,在JavaScript运行环境与原生代码(如Objective-C)之间建立安全、高效的通信桥梁。这项技术的核心价值在于弥合了Web应用的灵活性与原生设备能力(如摄像头、文件系统)之间的鸿沟,是实现Hybrid混合开发模式的关键。常见的实现方案包括URL Scheme拦截、JavaScriptCore框架以及WKWebView的MessageHandler。其中,WKWebView的MessageH

weixin_34297704的博客 450

关于NSTimer注意点

在使用NSTimer时候,我们习惯于在自后的dealloc中写入[self.timer invalidate];这样很多人就以为对象被清理的,但是真的被清理了吗?如果你的代码中建立了引用循环,那么dealloc方法将永远也不会被调用,计时器也永远也不会执行invalidate方法、因为运行循环会跟踪活跃的计时器对象和线程对象,所以仅在大妈中置为nil并不能销毁对象、要解决这个问题有两种方法: 第

NoPolun_iOS的博客 515

保姆级教程:用SSC Tool 5.13为先楫HPM6E00EVK生成8轴EtherCAT从站代码(含XML配置详解)

本文详细介绍了如何使用SSC Tool 5.13为先楫HPM6E00EVK开发板配置8轴EtherCAT从站,包括XML文件深度配置、代码生成与工程整合、烧录与调试技巧。教程涵盖从环境准备到性能优化的全流程,帮助开发者快速实现符合CIA402标准的8轴控制。

weixin_30352645的博客 254

内存管理——定时器问题

CADisplayLink、NSTimer的循环引用问题 CADisplayLink是QuartzCore框架下的的一种定时器,用在跟画图相关的处理当中。NSTimer大家应该很熟悉,是我们最常用的定时器。这两种定时器分别提供如下两个API + (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel; + (NSTi...

RUNNING-NIUER的博客 516

bugku渗透测试4-writeup

三、对这个主机进行全端口的扫描,发现存在22000 ssh端口,那么可以联想到时redis写入公钥来连接主机。又来了一个新的靶场,这个靶场打下来我觉得关键就是信息收集,走了很多的弯路。八、最后一个flag是要去看cms的数据库里面,这个数据库是sqlite。把这个数据库下载下来,登录密码是加密的,就直接用token。一、入口的漏洞是在登录的时候去抓包,修改action的值。六、没想到这个第一台机器的密码,在第二台里面。七、扫描第一台主机、使用之前的密码连接成功。获取最后一台主机的root密码。

vsdfbhsdhsdfh的博客 1294

【计算机视觉】pyrealsense2:Intel RealSense 深度相机的 Python 接口实战指南

pyrealsense2:Intel RealSense 深度相机的 Python 接口实战

weixin_43988131的博客 4350

APP内存管理

文章目录1、iOS程序的内存布局2、Tagged Pointer 标记指针问答拓展3、定时器3.1、NSTimer、CADisplayLink的使用注意点3.2、GCD定时器封装4、OC对象的内存管理4.1、引用计数的存储4.2、dealloc4.3、自动释放池4.3.1、autorelease 底层结构4.3.2、AutoreleasePoolPage1、链表关系2、实现原理3、触发逻辑5、问答拓展 1、iOS程序的内存布局 1、代码段:编译之后的代码 2、数据段 2.1、字符串常量:比如NSStr

chenzimin_blog 965

EDA环境下的LSF进阶调优:如何为Cadence/VCS等工具正确配置cgroups与daemon starter

本文深入探讨了在EDA环境中如何为Cadence/VCS等工具正确配置LSF的cgroups与daemon starter,解决资源分配与特殊应用兼容性问题。通过详细解析cgroups技术、daemon starter配置及混合资源管理策略,帮助提升LSF集群的效率和稳定性,特别适用于高性能计算和芯片设计场景。

weixin_33724046的博客 461

OC-内存管理-NSTimer

NSTimer 就是我们的定时器 可以满足我们很多业务需求 ,但是它会带来内存泄漏的隐患. 场景:在一个VC中设定一个全局定时器定时打印log,需要在pop该VC的时候 清除定时器 减少内存泄露 解决如下: 001 生命周期 处理 - (void)didMoveToParentViewController:(UIViewController *)parent{ NSLog(@"...

Coding_Physical的专栏 306

Opencv2.4.9源码分析——HoughCircles

图形可以用一些参数进行表示,标准霍夫变换的原理就是把图像空间转换成参数空间(即霍夫空间),例如霍夫变换的直线检测就是在距离-角度空间内进行检测。圆可以表示成: (x-a)2+(y-b)2=r2                   (1) 其中a和b表示圆心坐标,r表示圆半径,因此霍夫变换的圆检测就是在这三个参数组成的三维空间内进行检测。 原则上,霍夫变换可以检测任何形状。但复杂的形状需要的

赵春江的专栏 3万+

CAN总线介绍及硬件设计

1. CAN是什么 CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO*1 国际标准化的串行通信协议。 在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统 被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很 多,线束的数量也随之增加。为适应“减少线束的数量”、“......

sinat_15677011的博客 9989

OC底层探索(二十五)内存管理 --强引用

OC底层文章汇总 强引用 环境准备 此时有两个界面A、B,从A push 到B界面,在B界面中有如下定时器代码。当从B界面pop回到A界面时,发现定时器没有停止,其方法仍然在执行。 self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.timer.

正在注销账号的博客 292

华为OD新系统机试真题-8位LED控制器(C/C++/Py/Java/Js/Go)

华为OD新系统机试真题【8位LED控制器】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD新系统4月19号机试真题【8位LED控制器】

qq_45776114的博客 596

内存管理—MRC时代的手动内存管理

MRC时代的手动内存管理 iOS中是通过【引用计数】来管理OC对象的内存的。 一个新创建的OC对象引用计数默认是1,当引用计数减为0,OC对象就会销毁,其占用的内存空间会被系统释放。 调用retain会让OC对象的引用计数+1,调用release会让OC对象的引用计数-1。 内存管理的原则 当调用alloc、new、copy、mutableCopy方法返回了一个对象,再不需要这个对象时,要调...

RUNNING-NIUER的博客 911

openbmc 支持mctp over pcie(三)(支持作为endpoint)

本文介绍了在Intel BHS平台上支持MCTP over PCIe端点开发的调试方法。开发基于CodeConstruct和AspeedTech-BMC的开源项目,将内核驱动改为模块形式以便调试。内容包括DTS配置、驱动模块设置步骤,以及如何将BMC配置为端点设备。调试过程涉及内核模块加载、MCTPD启动、参数设置等关键命令,并展示了路由表、地址和邻居信息的测试结果。该方案仅为实验性工作,需注意停止其他使用/dev/aspeed-mctp0接口的应用。

wit_yuan的专栏 1553

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

oc中定时器的基本使用

// 时间间隔 调用的对象 调用的方法 用户信息 是否循环 1 [NSTimer scheduledTimerWithTimeInterval:1 2 target:self 3 selector:@selector(timerAc...

weixin_34082789的博客 404

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

<center>oc内存管理机制<:center>

详谈OC内存管理概念为什么要进行内存管理 因为移动设备的内存极其有限,所以每个APP所占的内存也是有限制的,当app所占用的内存较多时,系统就会发出内存警告,这时需要回收一些不需要再继续使用的内存空间,比如回收一些不再使用的对象和变量等。 内存管理类型 基础类型,如:int,float,char,bool,byte...等(内存在栈上,系统管理) 对象类型:继承于NSObjiect类的对象都

qq_32615009的博客 309

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

【OC】NSTimer

NSTimer 不是严格精确的,RunLoop 每跑一圈才检查一次 timer 有没有到时间,如果这一圈处理了耗时任务,timer 会延迟触发,所以 NSTimer 适合做轮播这种对精度要求不高的场景,不适合做精确计时。只是把“我们手里的引用”清掉 ,代表 timer 停了 ,如果还有别的强引用,timer 仍然活着。⚠️但是这只是一个示范展示一下思路,因为它只适用于nextToPage一个函数,有很大的局限性。前面创建的 timer 失去了引用,但是它还活着,还在 RunLoop 里继续跑,

2501_93869390的博客 394

iOS研发岗秋招笔试复盘:从OC内存管理到GCD,再到系统设计题

在移动开发技术栈中,Objective-C语言特性与系统框架的理解深度,往往是衡量iOS工程师核心能力的重要标尺。尤其是内存管理机制,如ARC下的循环引用、weak原理,以及多线程编程中GCD的队列与死锁问题,既是最基础的技术原理,也是实际工程中高频踩坑点。掌握这些概念不仅有助于构建稳定高效的App,在应对技术考核时同样至关重要。随着秋季招聘的展开,许多iOS岗位的笔试不再局限于纯算法,而是更侧重于考察候选人对运行时机制、RunLoop、属性修饰符等底层知识的综合运用。本文以一次真实的大厂秋招笔试为样本,完

weixin_30315435的博客 305

基于NStime计时器的实现

NStime计时器// // 计时器.m // OC知识点整理 // // Created by li on 17/2/27. // Copyright © 2017年 李文强. All rights reserved. //#import "计时器.h"@interface ___ () @property(weak,nonatomic)NSTimer*time; @end @implem

liwenqiang77的博客 496

Objective-C与JavaScript双向通信实战:WKWebView混合开发指南

在移动与桌面应用开发中,混合开发模式结合了原生性能与Web技术灵活性,成为构建复杂应用的重要架构。其核心原理在于通过JavaScript引擎桥接原生代码与Web视图,实现跨语言调用与数据交换。这一技术价值在于能充分发挥原生系统能力与Web生态优势,提升开发效率与跨平台一致性。在macOS/iOS平台上,WKWebView与JavaScriptCore是实现Objective-C与JavaScript互调的关键框架,广泛应用于需要内嵌Web界面、调用原生硬件功能或集成脚本引擎的场景。本文聚焦于WKWebVie

weixin_30279671的博客 404

ios笔试题算法_iOS面试题总结(持续更新)

过段时间打算跳槽,找了一些面试题来做,在这里做个总结方便review,希望能对要面试的童鞋有帮助。以下为面试题:运行以下代码会有什么结果NSString *str1 = @"str1";NSString*str2 = [NSString stringWithFormat:@"str1"];NSString*str3 = @"str1";NSLog(@"str1 == str2 --- %d", s...

weixin_39614276的博客 184

ios常见的内存泄露的原因

参考https://blog.youkuaiyun.com/clovejq/article/details/71107674ios中引入自动ARC后会产生内存泄露的情况就是循环引用,相互等待,造成内存不能释放。循环强引用也有可以用的地方,这样可以避免提前释放,但是自己一定要记住,用完后置为nil,让其自动释放二、Block循环引用【??strongself是为了防止内存提前释放,有兴趣的童鞋可深入了解,这里不...

qq_15156403的博客 1346

爱奇艺iOS笔试题深度解析:从Runtime到内存管理的工程实战

从iOS开发基础概念出发,围绕内存管理、多线程、Runtime等核心技术原理,解析大厂笔试题背后的工程思维。文章结合爱奇艺业务场景,深入讲解ARC循环引用、GCD多读单写、消息转发、Runloop卡顿监控及音视频优化等高频考点,帮助开发者构建系统化知识体系,提升面试竞争力。

weixin_33912638的博客 298

iOS开发常用技术底层实现(精简概述)

类的实例方法是存储在类的methodLists中,而类方法则是存储在元类的methodLists中,因此根据上图,NSObject的元类的superclass是指向Class,当调用[NSObject foo]的时候,因为这是一个类方法调用,所以从元类中查找签名为foo的方法,没有发现,然后再沿superclass继续查找,结果在Class中查找到该方法,于是调用该方法输出。因为在MRC下,block在创建的时候,它的内存是分配在栈(stack)上的,而不是在堆(heap)上,可能被随时回收。

lrbtony的博客 1075

【iOS】—— RunLoop线程常驻和线程保活

我们必须保证线程不消亡,才可以在后台接受时间处理,所以如果没有实现添加NSPort或者NSTimer,会发现执行完run方法,线程就会消亡,后续再执行touchbegan方法无效。实现了上面三个方法之一,就可以发现执行完了run方法,这个时候再点击屏幕,可以不断执行test方法,因为线程self.thread一直常驻后台,等待事件加入其中,然后执行。而我们有时会需要经常在一个子线程中执行任务,频繁的创建和销毁线程就会造成资源浪费,这时候就要用到RunLoop来使线程长时间存活了。

m0_62386635的博客 2262
上一篇: 「OC」内存管理——weak
下一篇: 「OC」源码学习——类的加载
小鹿撞出了脑震荡
博客等级 码龄3年 1107粉丝 97原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值