iOS底层系列之<46>--内存管理<2>定时器_ios 如何找到内存中的定时器-程序员宅基地

技术标签: objective-c  # iOS底层原理  ios  xcode  

1、定时器相关

看这个不错(https://www.jianshu.com/p/f641970d7439)

(a) 上一篇文章介绍了定时器相关内容,CADisplayLink 和NSTimer都是基于Runloop的定时器,但是Runloop的任务过于繁重的时候,定时器就不准了!
(b)所以,在项目中,尽量使用GCD定时器会更准确。

2、GCD定时器封装

类方法调用,但是我觉得可以封装成实例方法可能会更好一点,晚点再封装一次。
JHTimer.h文件

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//typedef (void)(^Task)(void);

@interface JHTimer : NSObject
+ (NSString *)excuteTask:(void (^)(void))task
             start:(NSTimeInterval)start
          interval:(NSTimeInterval)interval
         isRepeats:(BOOL)isRepeats
           isAsync:(BOOL)isAsync;

+ (void)cancelTimer:(NSString *)name;

+ (void)suspendTimer:(NSString *)name;

+ (void)restartTimer:(NSString *)name;
@end

NS_ASSUME_NONNULL_END

JHTimer.m文件

#import "JHTimer.h"

@implementation JHTimer

static NSMutableDictionary *timerDict;
static BOOL isStarted;
static BOOL isCanceled;
static BOOL isSuspended;
static BOOL isRestarted;
dispatch_semaphore_t semaphore;

+ (void)initialize {
    static dispatch_once_t onecToken;
    dispatch_once(&onecToken, ^{
        timerDict = [NSMutableDictionary dictionary];
        isStarted = NO;
        isCanceled = NO;
        isSuspended = NO;
        isRestarted = NO;
        semaphore = dispatch_semaphore_create(1);
    });
    
}

/**
 
 task: 任务
 start: 开始时间
 interval: 间隔时间
 isRpeats: 是否重复
 isMainQueue: 是否主线程
 
 return: 时间的标识符,开启一个定时器后,在需要的地方可以用一个标识符记录
 */
+ (NSString *)excuteTask:(void (^)(void))task start:(NSTimeInterval)start interval:(NSTimeInterval)interval isRepeats:(BOOL)isRepeats isMainQueue:(BOOL)isMainQueue {
    if (!task || start < 0 || interval < 0) {
        return nil;
    }
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    NSString *timerName = [NSString stringWithFormat:@"%@%@",@"EFTimer_",[self __currentTimeStr]];
    
    dispatch_queue_t queue = isMainQueue ? dispatch_get_main_queue() : dispatch_get_global_queue(0, 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, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);
    
    dispatch_source_set_event_handler(timer, ^{
        task();
        if (!isRepeats) {
            [self __cancelTimer:timerName];
        }
    });
    
    dispatch_resume(timer);
    
    
    timerDict[timerName] = timer;
    
    isStarted = YES;
    isCanceled = NO;
    isSuspended = NO;
    isRestarted = NO;
    
    dispatch_semaphore_signal(semaphore);
    
    return timerName;
}

/**
 根据创建的时候返回的标识符来取消定时器
 */
+ (void)cancelTimer:(NSString *)name {
    if ([timerDict objectForKey:name] == nil || isSuspended) {
        return;
    }
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    dispatch_cancel([timerDict objectForKey:name]);
    [timerDict removeObjectForKey:name];
    isStarted = NO;
    isCanceled = NO;
    isSuspended = NO;
    isRestarted = NO;
    
    dispatch_semaphore_signal(semaphore);
}

// 开启状态才可以
+ (void)suspendTimer:(NSString *)name {
    if ([timerDict objectForKey:name] == nil || !isStarted || isCanceled || isSuspended) {
        return;
    }
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    dispatch_suspend([timerDict objectForKey:name]);
    isStarted = YES;
    isCanceled = NO;
    isSuspended = YES;
    isRestarted = NO;
    
    dispatch_semaphore_signal(semaphore);
}

// suspend状态才可以
+ (void)restartTimer:(NSString *)name {
    if ([timerDict objectForKey:name] == nil || !isSuspended || isRestarted || isCanceled || !isStarted) {
        return;
    }
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    dispatch_resume([timerDict objectForKey:name]);
    isStarted = YES;
    isCanceled = NO;
    isSuspended = NO;
    isRestarted = YES;
    
    dispatch_semaphore_signal(semaphore);
}


+ (void)__cancelTimer:(NSString *)name {
    dispatch_cancel(timerDict[name]);
    [timerDict removeObjectForKey:name];
}

//获取当前时间戳
+ (NSString *)__currentTimeStr {
    NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
    NSTimeInterval time = [date timeIntervalSince1970]*1000;// *1000 是精确到毫秒,不乘就是精确到秒
    NSString *timeString = [NSString stringWithFormat:@"%.0f", time];
    return timeString;
}

@end

3、iOS程序的内存布局

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/JH_Cao/article/details/122743018

智能推荐

MySQL数据库管理基本操作(一)_myi-程序员宅基地

文章浏览阅读229次。目录一、数据库基本操作1.1 登录数据库1.2 查看数据库结构查看数据库信查看数据库中包含的表结构显示数据表的结构1.3 常用的数据库类型1.4 char和varchar的区别二、MySQL数据文件2.1 MYD文件2.2 MYI文件2.3 MyISAM存储引擎三、SQL语句3.1DDL数据定义语言创建新的数据库创建新的表删除指定的数据表删除指定的数据库3.2 DML数据操控语言向数据表中插入新的数据记录查询数据记录_myi

【计算思维】第14届蓝桥杯省赛计算思维U8组真题试卷_计算思维u8省赛-程序员宅基地

文章浏览阅读1.3k次,点赞23次,收藏21次。70 个小朋友参加夏令营活动,老师们组织小朋友做游戏,让他们站成一排,从 1 开始顺序报数,报奇数的 小朋友出局;剩下的小朋友位置不变,再次从 1 开始报数,报奇数的出局......按照这个规则继续下去,直 到剩一个小朋友为止。维维坐在第 1 列火车的 10 号车厢,奇奇坐在第 2 列火车的 10 号车厢。当维维和奇奇正好相遇时,第 1 列 火车的 14 号车厢,与第 2 列火车的( )号车厢相遇。下图中每个圆圈代表一个小朋友。将一个表面涂有颜色的正方体,分割成同样大小的 27 个小正方体,如下图所示。_计算思维u8省赛

记录配置Visual Studio 2022环境变量以及找不到nmake.exe文件的问题_未找到文件nmake-程序员宅基地

文章浏览阅读2.1k次,点赞3次,收藏7次。作者:奇版权:本作品采用「」许可协议进行许可。_未找到文件nmake

MongoDB索引详解-03-程序员宅基地

文章浏览阅读3.9k次,点赞2次,收藏5次。索引是一种用来快速查询数据的数据结构。B+Tree就是一种常用的数据库索引数据结构,MongoDB采用B+Tree 做索引,索引创建在colletions上。MongoDB不使用索引的查询,先扫描所有的文档,再匹配符合条件的文档。使用索引的查询,通过索引找到文档,使用索引能够极大的提升查询效率。按照索引包含的字段数量,可以分为单键索引和组合索引(或复合索引)。按照索引字段的类型,可以分为主键索引和非主键索引。按照索引节点与物理记录的对应方式来分,可以分为聚簇索引和非聚簇索引,其。_mongodb索引

在 Elasticsearch 中删除索引的语法是什么?_elasticsearch删除索引数据-程序员宅基地

文章浏览阅读190次。请注意,在实际操作前,请确保你已经正确备份了要删除的数据,因为删除索引操作不可逆。此外,如果需要删除所有索引(这是一项非常危险的操作,应当极其谨慎),可以使用。例如,如果你想要删除名为。_elasticsearch删除索引数据

如何写出一份优秀的PRD-文档篇_在线教育prd文档-程序员宅基地

文章浏览阅读285次。要用“作为一个用户(As a user),我希望(I want)什么功能,以(so that)满足什么商业价值“的标准格式描述,以讲清楚该需求的目标用户、功能和价值。一个功能设计是否合理,能否被设计和开发团队读懂,设计、开发出满足用户需求和业务需求的产品,都要依赖需求描述的合理性。如果有专业的交互设计师,这反而是对他设计的一种限制,以你的不专业影响了他的专业。上回讲到PRD撰写前的准备工作,包括摸清PRD的目标用户的习惯,深入了解本次用户的需求,根据INVEST和MVP原则、按照业务流程做功能拆分。_在线教育prd文档

随便推点

Android软键盘弹出不影响布局的方法_android 虚拟键盘弹出收回不影响布局-程序员宅基地

文章浏览阅读2.3k次。【转载】http://geyubin.iteye.com/blog/1297637在原有的基础上根据原始文档的说明,对其中的翻译进行了修订,以便读起来更顺畅,更容易理解。attributes:android:windowSoftInputModeActivity如何与软键盘交互。这个属性的设置会影响两件事情:软键盘的状态——隐藏或显示——当活动(Activi_android 虚拟键盘弹出收回不影响布局

精神病人思维广 ——读《天才在左,疯子在右有感》_天才在左疯子在右时间静止-程序员宅基地

文章浏览阅读1.9k次。请容许我用这么一句调侃的语句来作为本篇文章的标题,因为看了这本书后我强烈的感受到了这句话。本书实际上是访谈笔记,只不过访谈的对象比较特殊——精神病人,他们有着天马行空的思想,超强的逻辑思维让我这个程序员感到惭愧,不得不赞叹一声:精神病人就是思维广! 一个月前读完本书,现在回想起来,脑袋里不停冒出几个词:四维虫子、生化奴隶、玛雅文明等,简单的词语里包含着丰富而且又匪夷所思的内容,下面_天才在左疯子在右时间静止

基于微信小程序餐厅点餐系统设计与实现(源码+lw+部署文档+讲解等)-程序员宅基地

文章浏览阅读353次,点赞8次,收藏12次。博主介绍:全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战精彩专栏 推荐订阅2023-2024年最值得选的微信小程序毕业设计选题大全:100个热门选题推荐2023-2024年最值得选的Java毕业设计选题大全:500个热门选题推荐Java精品实战案例《500套》微信小程序项目精品案例《500套》文末获取源码+数据库。

Django入门_通过什么语句把我们新定义的app加到settings.py中的install_apps中-程序员宅基地

文章浏览阅读1.1k次。前几天看了Python的一些基础知识,然后又了解了一下相关的web框架,比较主流的有Django和Flask,比较了一下之后决定学Django,基础供能比较完善,对于初学者应该更友好一些。一边跟着大神的博客做,一边整理了这篇博客,没什么新的东西,只是我个人的学习笔记,其中一些部分我专门修改成不太一样的东西,以免学习过程中不求甚解的忽略。总的来说大神这篇博客非常不错,如果你是刚学习了Python的基础_通过什么语句把我们新定义的app加到settings.py中的install_apps中

zuul源码分析_zuulhandlermapping-程序员宅基地

文章浏览阅读424次。Zuul请求处理流程zuul框架在处理http请求时,由springmvc所管理,一起看下具体流程:请求由DispatchServlet分发,查找HandlerMapping,zuul框架定义了ZuulHandlerMapping,它是将路由规则绑定到了ZuulController上面; 查找HandlerMappingAdapter映射器处理器,由SimpleControllerH..._zuulhandlermapping

packetfence 7.2网络准入部署(二)-程序员宅基地

文章浏览阅读658次。今天呢先说下packetfence部署的环境; 关于使用方法之前的帖子有介绍,一定要看哦 https://blog.csdn.net/qq_18204953/article/details/80708303 官网部署参考网址 https://packetfence.org/doc/PacketFence_Installation_Guide.html 一、准备工作(服务器、设备) 需求: 1、ce..._packetfence 部署