nstimer循环引用_解决NSTimer循环引用导致内存泄漏的六种方法
demo放在了GitHub
内存泄漏的原因:
self强引用timer。timer添加在runloop上,只要timer不销毁self就销毁不了。当然了你可以选择在viewWillDisappear中销毁timer。但是定时器页面不一定都是pop到上一个页面,也有可能push新页面,也有可能是进入后台,这样我们希望重新回到定时器页面的时候,定时任务还依旧是执行状态。所以invalidate放到viewWillDisappear是不合理的,唯一合理的地方就是定时器页面销毁的时候销毁timer。这就引出了以下三种解决方法。
第一、二种方法是在合适的时机销毁timer,干掉强引用。
第三种方法是自定义timer,弱引用timer,从源头上就不形成循环引用,更不会导致内存泄漏。
一、离开当前控制器销毁NSTimer
didMoveToParentViewController方法了解一下
@interface WLZSecondController ()
@property (nonatomic, strong)UILabel *label;
@property (nonatomic, assign)int repeatTime;
//第一、二种的属性
//@property (nonatomic, strong)NSTimer *timer;
//第三种方法的属性
@property (nonatomic, strong)WLZTimer *timer;
@end
@implementation WLZSecondController
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
self.repeatTime = 60;
self.label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
self.label.text = @"60";
self.label.textColor = [UIColor blackColor];
[self.view addSubview:self.label];
[self createTimer];
}
#pragma mark - 第一种方法
- (void)createTimer{
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(change) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)didMoveToParentViewController:(UIViewController *)parent{
if(parent == nil){
[self.timer invalidate];
}
}
- (void)change{
self.repeatTime --;
self.label.text = [NSString stringWithFormat:@"%d",self.repeatTime];
if(self.repeatTime == 0){
[self.timer invalidate];
}
}
- (void)dealloc{
NSLog(@"dealloc");
}
复制代码
二、自定义返回按钮销毁NSTimer
这种方法就需要禁止掉侧滑返回手势。
#pragma mark - 第二种方法 这里只是随意创建了一个button,具体的图片、文案可以自己调试。
- (void)createTimer{
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(change) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)viewWillAppear:(BOOL)animated{
[self changeBackBarButtonItem];
}
- (void)changeBackBarButtonItem{
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(invalidateTimer)];
}
- (void)invalidateTimer{
[self.timer invalidate];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)change{
self.repeatTime --;
self.label.text = [NSString stringWithFormat:@"%d",self.repeatTime];
if(self.repeatTime == 0){
[self.timer invalidate];
}
}
- (void)dealloc{
NSLog(@"dealloc");
}
复制代码
三、自定义timer,压根不形成循环引用
自定义timer类
.h文件
@interface WLZTimer : NSObject
@property (nonatomic, weak)id target;
@property (nonatomic, assign)SEL selector;
///创建timer
- (instancetype)initWithTimeInterval:(NSTimeInterval)interval Target:(id)target andSelector:(SEL)selector;
///销毁timer
- (void)closeTimer;
@end
.m文件
@interface WLZTimer ()
@property (nonatomic, strong)NSTimer *timer;
@end
@implementation WLZTimer
- (instancetype)initWithTimeInterval:(NSTimeInterval)interval Target:(id)target andSelector:(SEL)selector{
if(self == [super init]){
self.target = target;
self.selector = selector;
self.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(dosomething) userInfo:nil repeats:YES];
}
return self;
}
- (void)dosomething{
//这里是为了不阻塞主线程
dispatch_async(dispatch_get_main_queue(), ^{
id target = self.target;
SEL selector = self.selector;
if([target respondsToSelector:selector]){
[target performSelector:selector withObject:nil];
}
});
}
- (void)closeTimer{
[self.timer invalidate];
self.timer = nil;
}
- (void)dealloc{
NSLog(@"WLZTimer dealloc");
}
@end
复制代码
自定义timer在主线程异步执行任务不明白原因的话,可参考文章iOS线程、同步异步、串行并行队列
创建timer的时候就用自定义的类就可以
#pragma mark - 第三种方法
/*
* 与第前两种不同的是:前两种只是在合适的时机解决循环引用,
* 第三种根本就不会造成循环引用,可以封装起来供多个地方使用,而且遵循单一职责原则
*
*/
- (void)createTimer{
self.timer = [[WLZTimer alloc] initWithTimeInterval:1 Target:self andSelector:@selector(change)];
}
- (void)change{
self.repeatTime --;
self.label.text = [NSString stringWithFormat:@"%d",self.repeatTime];
if(self.repeatTime == 0){
[self.timer closeTimer];
}
}
- (void)dealloc{
[self.timer closeTimer];
NSLog(@"dealloc");
}
复制代码
2019/1/17 更新
四、定义中介继承NSObject进行消息转发消除强引用NSTimer
+ (instancetype)proxyWithTarget:(id)aTarget{
TFQProxy *proxy = [[TFQProxy alloc] init];
proxy.target = aTarget;
return proxy;
}
//自己不能处理这个消息,就会调用这个方法来消息转发,return target,让target来调用这个方法。
- (id)forwardingTargetForSelector:(SEL)aSelector{
if([self.target respondsToSelector:aSelector]){
return self.target;
}
return nil;
}
复制代码self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[TFQProxy proxyWithTarget:self] selector:@selector(repeatAction:) userInfo:nil repeats:YES];
复制代码
五、定义中介继承NSProxy进行消息转发消除强引用NSTimer
+ (instancetype)proxyWithTarget:(id)aTarget{
TFQProxySubclass *proxy = [TFQProxySubclass alloc];
proxy.target = aTarget;
return proxy;
}
//为另一个类实现的消息创建一个有效的方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
return [self.target methodSignatureForSelector:sel];
}
//将选择器转发给一个真正实现了该消息的对象
- (void)forwardInvocation:(NSInvocation *)invocation{
[invocation invokeWithTarget:self.target];
}
复制代码self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[TFQProxySubclass proxyWithTarget:self] selector:@selector(repeatAction:) userInfo:nil repeats:YES];
复制代码
tips:五比四效率高,因为5是系统的类,直接进行消息转发,4会走几条弯路才会到消息转发那个方法
六、GCD创建定时器
/**
GCD创建定时器
@param task 定时器内容
@param interval 执行间隔
@param repeat 是否重复
@param async 是否异步
@param identifier 定时器唯一ID
@return 返回定时器唯一ID,销毁的时候用
*/
+ (NSString *)schedleTask:(void (^)(void))task interval:(NSTimeInterval)interval repeat:(BOOL)repeat async:(BOOL)async reuseIdentifier:(NSString *)identifier{
dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
//穿件定时器
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//开始时间
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, 0*NSEC_PER_SEC);
//设置各种时间
dispatch_source_set_timer(timer, start, interval*NSEC_PER_SEC, 0);
//设置回调
dispatch_source_set_event_handler(timer, ^{
task();
if(!repeat){
[self cancelTimer:identifier];
}
});
//启动定时器
dispatch_resume(timer);
timerDict[identifier] = timer;
return identifier;
}
+ (void)cancelTimer:(NSString *)identifier{
dispatch_source_cancel(timerDict[identifier]);
[timerDict removeObjectForKey:identifier];
}
复制代码__weak typeof(self) weakSelf = self;
self.timerIdentifier = [TFQGCDTimer schedleTask:^{
[weakSelf repeatAction:nil];
} interval:1 repeat:YES async:NO reuseIdentifier:@"identifier"];
复制代码
nstimer循环引用_解决NSTimer循环引用导致内存泄漏的六种方法相关推荐
- 解决Solaris应用程序开发内存泄漏问题
作者: 李凌云,张一峰(laoeyu) 内存泄漏是应用软件开发过程中经常会遇到的问题,应用长期内存泄漏会占用大量操作系统内存资源,直接导致应用程序运行不稳定,严重时甚至还会影响到操作系统的正常运行.为 ...
- python会不会内存泄露_总结python 三种常见的内存泄漏场景
概要 不要以为 Python 有自动垃圾回收就不会内存泄漏,本着它有"垃圾回收"我有"垃圾代码"的精神,现在总结一下三种常见的内存泄漏场景. 无穷大导致内存泄漏 ...
- 利用classloader同一个项目中加载另一个同名的类_线程上下文类加载器ContextClassLoader内存泄漏隐患...
前提 今天(2020-01-18)在编写Netty相关代码的时候,从Netty源码中的ThreadDeathWatcher和GlobalEventExecutor追溯到两个和线程上下文类加载器Cont ...
- 会不会导致内存泄漏_可能会导致.NET内存泄露的8种行为
原文连接:https://michaelscodingspot.com/ways-to-cause-memory-leaks-in-dotnet/ 作者 Michael Shpilt.授权翻译,转载请 ...
- python elif报错_解决python循环的elif报错的方法
解决python循环的elif报错的方法 发布时间:2020-08-05 15:36:51 来源:亿速云 阅读:96 作者:小新 解决python循环的elif报错的方法?这个问题可能是我们日常学习或 ...
- sqlserver2008未将对象引用设置到对象的实例_面试官:ThreadLocal 的内存泄漏是弱引用导致的,你确定?...
面试官:ThreadLocal 了解吗? Python 小星:线程局部变量,多线程下能保证各个线程的变量相对独立于其他线程的变量. 面试官:那你说下它是如何保证线程隔离的? Python 小星:每个线 ...
- python字典弱引用_如何使用弱引用优化 Python 程序的内存占用?
Python 的垃圾回收机制通过引用计数来决定一个对象要不要被回收.当一个对象被引用次数为0时,它就会被作为垃圾回收从而释放 Python 内存.但有些情况下,我们的代码可能在不经意间导致某些实际上我 ...
- python有几种循环语句_[14] Python循环语句(一)
1. 概述 今天我们介绍循环语句,和条件判断一样,我们从流程图开始看起.首先看一下学习计划列表,粗体为已学,斜体为新增或修改内容.计算机编程的原理简要介绍 集成开发环境PyCharm 变量名.数字.字 ...
- for循环递减_讲讲关于循环的那些事
每个人一生中都至少应该获得一次全场起立鼓掌的机会,因为我们都曾胜过这个世界.-R.J.帕拉西奥<奇迹男孩> 导言:希腊哲学家Zeno曾经说"运动是不可能的.由于运动的物体在到达目 ...
最新文章
- 任正非签发最新电邮:过去我们是为了赚点小钱,现在是要战胜美国
- linux系统知识 - 信号基础
- Python中print()使用格式示例收集
- 面试必会系列 - 1.6 Java 垃圾回收机制
- android 编译共享ccache的缓存
- Spring Boot 是什么,有什么用。
- 【今日CS 视觉论文速览】 25 Jan 2019
- spring bean
- JavaEE Tutorials (9) - 运行持久化示例
- python中间件有哪些_python_21(Django中间件)
- 用CST进行多物理仿真,热仿真结果有误
- ArcEngine旋转IRotateTracker
- 诸葛 理解产品、交互和运营
- Mac设置顶部菜单栏技巧?
- l003 Driller Augmenting Fuzzing Through Selective Symbolic Execution_2016_NDSS学习笔记
- pta——出生年,查验身份证(c语言)
- Remove Duplicates
- 怎么安装打印机驱动?有没有快捷的方法?
- 藏拙的搜索引擎技术是2008年全宇宙最耀眼的搜索引擎技术吗?
- 基于PHP的快递查询免费开放平台案例-快宝开放平台
热门文章
- 识别图片并可视化_识别交通锥,特斯拉驾驶信任提升的一小步
- 打开浏览器提示下载解决方法
- 使用chrome下载m3u8视频
- Web系统中Mic设备的应用实例
- Reporting service个人使用经验
- .NET Core 配置Configuration杂谈
- 简单记事本及目录树形图的Java实现
- springcloud(三):服务提供与调用
- Mysql报错Fatal error: Can#39;t open and lock privilege tables: Table #39;mysql.host#39; doesn#39;t...
- #大学生活#锐捷客户端与VMWare