封装一个GCD定时器,彻底解决定时器循环引用、释放时机问题

GCD定时器接口封装 个人最近学习回顾了定时器相关内容知识点,这里做一下笔记: 注:接口内容包含了 创建定时器及执行定时器任务; 取消定时器任务; 关键代码加锁:定时器唯一标识操作加锁 .h文件 #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface GHTimer : NSObject +(NSString *)execTask:(void(^)(void))task ... 阅读详情

相信大家在开发中都会使用到定时器, 但又常常对定时器的循环引用问题, NSTimer 释放时机的选择上,劳神费力! 读了本文,这些再也不是问题! 
关于 NSTimer 创建定时器的方法,我就不多做描述了,网上很多例子,但也总觉得很麻烦。本文主要讲使用GCD的方法。

今天在重构代码的时候,发现项目中好几个地方都用到了定时器,就想着封装一个定时器的方法,以后用着方便,也可以丰富自己的工具类库;写的时候,发现 GCD定时器方法并不被执行,细看之下感觉跟以前写的也没什么区别啊,但为什么就没有执行呢?

void dispatchTimer(double timeInterval, void (^handler)())
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
    dispatch_source_set_timer(timer,dispatch_time(DISPATCH_TIME_NOW, 0),1.0*NSEC_PER_SEC, 0); 
    dispatch_source_set_event_handler(timer, ^{

            dispatch_async(dispatch_get_main_queue(), ^{
                handler();
            });
    });
    dispatch_resume(timer);
}

细想一下,原来是因为 timer 是个局部变量,当函数执行完之后,会被自动释放,也就不会再执行handler的方法了;忽略了以前在控制器中实现的时候 都会用一个属性来记录下 timer;

网上的一些文章也大都会说 ,要用 全局变量记录下timer,防止timer被释放,其实不然。 
http://www.cnblogs.com/dongjue/p/5804521.html 
http://www.jianshu.com/p/3a300fefa627

如何保证 在执行 handler 时 timer 不被释放呢? 我需要对 timer 有一个强引用, 如果按原来的思路在用到定时器的类里用属性记录下timer,把timer作为参数传入,我就需要每次调用这个函数的时候先创建好一个 定时器,那么封装的这个方法也就没有什么意义了。

后来想到一个方法: 给回调 handler 里添加一个 dispatch_source_t timer 参数,在 dispatch_source_set_event_handler 的block里回调 handler时将局部变量 timer 回传; 这个时候dispatch_source_set_event_handler的 block 相当于对 timer 进行了一次强引用,这样一来 timer 也就不会被过早释放了; 
如有对 block 内存管理不慎明白的可以看一下这篇文章 
block对外部变量的内存管理

void dispatchTimer(double timeInterval,void (^handler)(dispatch_source_t timer))
{
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t timer =dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0, 0, queue);
        dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (uint64_t)(timeInterval *NSEC_PER_SEC), 0);
        // 设置回调
        dispatch_source_set_event_handler(timer, ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                //编译时block会对timer对象强引用,使timer不会被过早释放
                if (handler) handler(timer);
            });
        });
        // 启动定时器
        dispatch_resume(timer);
}

在使用的时候才发现,handler 里添加的那个 dispatch_source_t timer 参数,原来也是必须要有的,我需要 timer对象 来结束定时器或销毁定时器。o(╯□╰)o 啊,原以为解决了一个问题,没想到这是必须的一个参数。o(╯□╰)o o(╯□╰)o o(╯□╰)o

后来考虑到,当使用定时器的对象销毁的时候,可能并没有手动对定时器进行释放,于是又完善了一下, 添加了一个 target 参数,当 target 销毁时,就将 定时器销毁。哈哈,终于功德圆满了。

先看看使用效果吧: 

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    __block int timeCount = 60;
    dispatchTimer(self, 1.0, ^(dispatch_source_t timer) {

        if (timeCount < 0) {
            dispatch_source_cancel(timer);
        } else {
            NSLog(@"%d", timeCount);
            timeCount -= 5;
        }
    });

}
  • 注意:

调用 dispatch_source_cancel(timer); 就会将 timer 对象销毁,唯一要考虑的就是 timer 的结束时机;即使你不主动释放timer, 也不会造成循环引用哦 O(∩_∩)O~

上源码:

/**
 开启一个定时器

 @param target 定时器持有者
 @param timeInterval 执行间隔时间
 @param handler 重复执行事件
 */
void dispatchTimer(id target, double timeInterval,void (^handler)(dispatch_source_t timer))
{
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_source_t timer =dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0, 0, queue);
        dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), (uint64_t)(timeInterval *NSEC_PER_SEC), 0);
        // 设置回调
    __weak __typeof(target) weaktarget  = target;
        dispatch_source_set_event_handler(timer, ^{
            if (!weaktarget)  {
                dispatch_source_cancel(timer);
            } else {
                dispatch_async(dispatch_get_main_queue(), ^{
                    if (handler) handler(timer);
                });
            }
        });
        // 启动定时器
        dispatch_resume(timer);
}
  •  

将此函数copy到你的工具类中就可以放心使用了。

妈妈再也不用担心 我的定时器的循环引用问题了! 哈哈,有没有很 激动!

深入理解iOS循环定时器的实现与管理 NSTimer是Foundation框架中用于实现定时功能的一个类。它可以安排一个定时器事件在未来某个时间点触发,并重复触发。NSTimer类是Objective-C语言中用于定时器的实现方式之一,它允许开发者设定一个时间间隔,在这个时间间隔后执行一个指定的方法。NSTimer提供了多种创建定时器的方式,包括使用时间间隔和使用日期对象触发。是GCD中用于创建同步或异步任务的类型,可以用来实现包括定时器在内的多种类型的事件源。该类型在底层使用高效的数据结构,利用操作系统的调度器来处理任务。 阅读详情

相关推荐

深入解析iOS GCD计时器的封装与应用

首先,我们需要理解什么是并发编程。并发编程是通过同时执行多任务来提高应用程序性能的技术。在多核处理器上,真正的并行执行可以缩短程序执行的时间。在单核处理器上,多任务可以通过快速交替执行来实现,从而给用户一种程序正在同时执行多个任务的错觉。让我们通过一个案例来分析GCD Timer的性能优化。假设一个应用需要每10秒执行一次数据库查询操作,传统方法可能需要在主线程上设置定时器,但这样会导致界面卡顿。通过GCD Timer,我们可以在后台队列上执行此任务,然后通过代理或闭包将结果传回主线程。

weixin_42510243的博客 663

GCD知识补充.目标队列+GCD循环

通常我们会用for循环遍历,但是GCD给我们提供了快速迭代的函数dispatch_apply。 dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。 如果是在串行队列中使用 dispatch_apply,那么就和 for 循环一样,按顺序同步执行。可这样就体现不出快速迭代的意义了。 我们可以利用并发队列进行异步执行。比如说遍历 0~10 这10个数字,for 循环的做法是每次取出一个元素,逐个遍历。dispatch_apply 可以 在多个线程中同时(异步)

gcs的博客 1430

iOS开发者必备:从越狱设备里提取IPA的三种方法(含Filza、iTools实战)

本文详细介绍了iOS开发者从越狱设备提取IPA文件的三种实用方法,包括使用Filza文件管理器、AFC2配合桌面工具以及命令行高级技术。内容涵盖环境准备、操作步骤和技术原理,帮助开发者高效获取应用包文件,适用于逆向工程、测试分析等场景。

weixin_28717969的博客 804

dispatch_source_t定时器

定时器简述 在iOS中,计时器是比较常用的,用于统计累加数据或者倒计时等,计时器大概有那么三种,分别是: NSTimer CADisplayLink dispatch_source_t 比较 1、NSTimer特性: 存在延迟,不管是一次性的还是周期性的timer的实际触发事件的时间,都会与所加入的RunLoop和RunLoop Mode有关,如果此RunLoop正在执行一个连续性的运算,timer就会被延时出发。重复性的timer遇到这种情况,如果延迟超过了一个周期,

u013712343的博客 3333

DSPACE仿真平台的使用

DSPACE仿真平台的使用 文章目录DSPACE仿真平台的使用1.ModelDesk新建项目2.MotionDesk引入项目3.ControlDesk引入项目4.项目整合运行 我们使用DSPACE仿真平台软件来模拟小汽车的自动驾驶。 总共需要用到DSPACE大类下的三个软件:ModelDesk、ControlDesk、MotionDesk。 1.ModelDesk新建项目 在桌面上点击ModelDesk图标,打开DSPACE—ModelDesk 选择 File 标签,侧边栏选择 New,点击 New A

qq_42582489的博客 7526

IPSec Over GRE和GRE Over IPSec技术

IPSec Over GRE 和 GRE Over IPSec 一. 技术介绍 IPsec     主要作用是对数据进行加密,因为他能提供所有有时候被单独用作实现加密的一种方法!IPsec建立的是一个逻辑隧道,并不是真正意义上的隧道!并且不能提供路由功能,因为IPsec不支持非ip流量,也不支持广播(组播)! GRE     (通用路由封装)能很好的提供一个真正意义上的点对点的隧道,GRE是一种三层VPN封装技术。GRE可以对某些网络层协议(如IPX、Apple Talk、IP等)的报文进行封装,使封装后

qq_56228347的博客 9866

使用GCD实现简单循环

//设置时间间隔(每五秒) dispatch_source_t _timer; NSTimeInterval period = 5.f; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer

dustinqi的博客 981

从零开始:使用英伟达nx开发板部署目标检测算法的全过程

英伟达nx开发板烧录、依赖安装、开发板联网

weixin_51192760的博客 3760

GCD迭代(多线程循环遍历)

开子线程和主线程一起完成遍历任务,任务的执行是并发的 第一个参数:遍历的次数 第二个参数:队列(并发队列) 第三个参数:index 索引(相当于for循环中的i) dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) { <#code#> }); ...

weixin_44729579的博客 441

出个AI8051U的PWM例程 | 3相互补SPWM变频驱动,PWMA-3个相位差120度的PWM互补输出

通道2输出: P2.2, PWMA2P输出, P2.3, PWMA2N输出, PWMA2相对PWMA1滞后120度相位.通道3输出: P2.4, PWMA3P输出, P2.5, PWMA3N输出, PWMA3相对PWMA1滞后240度相位.先别修改程序, 直接下载"Objects"里的"pwm.hex"测试, 下载时选择主频24MHz.主频32.768MHz, PWM周期1422, 刷新频率23040Hz,死区0.5us,通道1输出: P2.0, PWMA1P输出, P2.1, PWMA1N输出。

USB_CAN_8051的博客 689

零代码实战:基于Coze平台打造测试用例生成AI助手

本文详细介绍了如何利用Coze平台零代码打造测试用例生成AI助手,通过智能体和工作流配置,实现需求文档自动解析与结构化测试用例生成。该方案显著提升测试效率,特别适合敏捷开发中频繁变更的需求场景,帮助测试工程师快速应对挑战。

weixin_42526015的博客 209

iOS多线程---GCD

Grand Central Dispatch(GCD)是苹果封装的一套C的API,异步执行任务的技术之一。它可以自动管理线程的创建,调度和销毁等功能,无需开发者自己实现。苹果官方对GCD的说明:开发者只需要定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。用于管理追加的Block的C语言层实现的FIFO队列Atomic函数中实现的用于排他控制的轻量级信号用于管理线程的C语言层实现的一些容器。

旭日的博客 1397

Linux ARM平台开发系列讲解(摄像头V4L2子系统) 2.12.7 摄像头V4L2驱动开发流程总结

1.概述前几章对摄像头V4L2驱动开发各个环节做了展开分析,这一章节就做一个汇总,用来回顾前几章得知识点,概括一下摄像头开发得流程,本次使用RK3399。2.SensorSub-device开发移植Sensor驱动位于目录下,注意到本章节所描述的是具有属性的sensor驱动,故目录下的驱动并不适用。在结构下,Sensor一般作为sub-device并通过pad与cif、isp或者mipiphy链接在一起。本章主要介绍Sensor驱动的代码[1],dts配置,及如何验证sensor5个部分。...

DSMGUOGUO的博客 3726

CADisplayLink、NSTimer、GCD定时器

IOS的常用的定时器有NSTimer、CADisplayLink、GCD定时器。其中NSTimer、CADisplayLink有以下特点:依赖RunLoop,某些方法创建时需要手动把定时器添加到RunLoop中如果是通过target:selector方式创建的,需要注意定时器会对target产生强引用。由于依赖RunLoop,如果某个RunLoop循环中有大量的耗时操作,则定时器的回掉可能不会准确的在定时间隔内触发。GCD定时器由以下特点:依赖于操作系统内核的,定时精度相对高。

qq_27945615的博客 1128

YOLOv8与注意力机制融合:EMA(高效多维注意力)的原理剖析与实战应用

本文介绍了如何在YOLOv8目标检测模型中集成EMA(高效多尺度注意力)机制。主要内容包括:EMA注意力机制的工作原理解析,展示其多尺度特征融合和高效计算特性;详细的环境配置与依赖安装指南;EMA模块的完整代码实现,包含分组处理、空间注意力权重计算和特征融合等关键步骤;以及将EMA集成到YOLOv8网络结构的方法。通过添加EMA模块,可以提升模型对关键特征的关注能力,有效抑制背景干扰,从而增强目标检测性能。本教程提供了从原理到实现的完整解决方案。

m0_64661770的博客 146

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

iOS常见三种定时器-NSTimer、CADisplayLink、GCD定时器

文章链接:https://juejin.cn/post/6863452695144071175   在iOS开发过程当中,我们经常会直接或间接地使用到定时器,iOS系统中,带有延迟性操作的函数都是基于NSTimer,CADisplayLink或者GCD定时器来实现的。本文主要也是围绕这三种定时器展开,最后封装一个简单易用的定时器库。 1、NSTimer定时器 NSTimer是基于NSRunloop的实现定时器,在使用NSTimer过程当中,应该关注两个问题 一、直接使用NSTimer定时器,可能存在循环

henry_lei的博客 936

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ios xcode 定时器_GitHub - ZhenKaiJia/Timer: iOS常用计时器包括NSTimer、CADisplayLink、GCD定时器。主要介绍三种定时器的使用,以及全局定时器...

一、介绍iOS常用计时器包括NSTimer、CADisplayLink、GCD定时器。本次主要介绍三种定时器的使用,以及全局定时器的封装。过程主要涉及知识点定时器、多线程、锁、消息转发。二、使用1、Timer第一种 这种会导致循环引用,因为self强引用timer,timer强引用target,会导致定时器释放失败。self.timer = Timer.scheduledTimer(timeInt...

weixin_39936310的博客 504

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

GCD实践——GCD定时器

在实际开发中,我们可能常常使用NSTimer作为我们的定时器,但是有了GCD之后,使用定时器就变得更加简单了。下面我们来简单实现一下。 (1)在项目中,导入GCD的源代码。然后在ViewController中实现如下: #import "ViewController.h" #import "GCD.h" @interface ViewController () @property(stro

乞力马扎罗的雪CYF的博客 2925

解决 NSTimer 循环引用问题

1、NSProxy @implementation FirViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor orangeColor]; self.navigationItem.leftBarButtonItem = [[...

iOSZZZh的博客 351

iOS - GCD中的定时器

GCD定时器优点:① GCD定时器不受RunLoop约束② 比NSTimer更加准时。 GCD定时器用法 敲 dispatch_source_选择第一个提示的代码段,注意不是dispatch_time,而是dispatch_source_. 01.png 敲完dispatch_source并选择第一个,就会有下面代码段的提示: 02.png 这里参数比较多,我们先来看下各个参数...

u010707262的博客 869

GCD定时器封装OC&Swift

前言 iOS开发中,经常需要使用到定时器,使用NSTimer很容易出现内存泄漏,在此简单封装GCD定时器。 作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:812157648,不管你是小白还是大牛欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长! OC版本 @interface CLGCDTimer () /**队列*/ @property (nonatomic, strong) dispatch_queue_t queue; /**定时器*

iOSxiaodaidai的博客 541

iOS GCD定时器的使用

GCDTimer类,使用时导入该类即可。

iOS15011015的博客 1311

封装GCD定时器

封装GCD定时器 #import "WSTimer.h" @implementation WSTimer static NSMutableDictionary *timers; dispatch_semaphore_t semaphore; + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ timers = [NSMutableDictionar

weixin_42058333的博客 287

swift GCD-定时器(DispatchSourceTimer)注册登陆组件封装

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34402090的博客 377

GCD定时器封装

前言 iOS开发中,经常需要使用到定时器,使用NSTimer很容易出现内存泄漏,在此简单封装GCD定时器。 1.定时器方法 - (instancetype)initDispatchTimerWithTimeInterval:(double)interval delaySecs:(float)delaySecs ...

weixin_34342578的博客 228
上一篇: ssh密钥认证原理
下一篇: 从头到尾彻底理解KMP
runtime666
博客等级 码龄10年 41粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值