NSTimer、CADisplayLink 内存泄漏
NSTimer、CADisplayLink 内存泄漏
内存泄漏的原因
CADisplayLink 要用 Taget 和 Selector 初始化,NSTimer 也可以用类似的方法初始化。这样初始化之后,NSTimer 或 CADisplayLink(以下把两者统称为 CADisplayLink)会强引用 Target。当 CADisplayLink 加入 NSRunLoop 中,NSRunLoop 会强引用 CADisplayLink。直到 CADisplayLink 调用 invalidate 方法,CADisplayLink 才会被 NSRunLoop 移除,CADisplayLink 也不再强引用 Target。
通常在 UIViewController 或 UIView 中创建 CADisplayLink,Target 是 UIViewController 或 UIView。如果只在 UIViewController 或 UIView 的 dealloc 方法中调用 CADisplayLink 的 invalidate 方法,会造成内存泄漏。因为 NSRunLoop 一直都在,CADisplayLink 不释放,Target 被强引用,Target 的 dealloc 方法不会被调用,CADisplayLink 的 invalidate 方法也不被调用,CADisplayLink 不会从 NSRunLoop 中移除。参见示意图,实线箭头表示强引用
NSRunLoop —> CADisplayLink —> Target
另外,为了在 Target 的 dealloc 方法中调用 CADisplayLink 的 invalidate 方法,CADisplayLink 可能作为 Target 的属性被强引用,这就形成 CADisplayLink 和 Target 的互相强引用,造成内存泄漏。
解决方法
改变 invalidate 方法的调用时机
例如,在 UIViewController 的 viewDidDisappear: 方法中,或者在 CADisplayLink 实现的动画结束后,或者在其他合适的时机调用 CADisplayLink 的 invalidate 方法。这样就把 CADisplayLink 从 NSRunLoop 中移除,CADisplayLink 也不再强引用 Target。即使 Target 强引用 CADisplayLink,在 Target 被释放后,CADisplayLink 也会被释放。
通过 NSProxy 避免 CADisplayLink 对 Target 的强引用
如果找不到合适的时机调用 CADisplayLink 的 invalidate 方法,那么还是在 dealloc 方法中调用 invalidate 方法,同时用 NSProxy 避免 CADisplayLink 对 Target 的强引用。开源库 FLAnimatedImage 中的 FLAnimatedImageView 用 FLWeakProxy,避免 CADisplayLink 的强引用。公开的方法只有初始化方法
@interface FLWeakProxy : NSProxy
+ (instancetype)weakProxyForObject:(id)targetObject;
@end
使用时,把 self 套一层 FLWeakProxy 即可防止 self 被 CADisplayLink 强引用。参见示意图,虚线箭头表示弱引用
NSRunLoop —> CADisplayLink —> Proxy - - > self
FLWeakProxy *weakProxy = [FLWeakProxy weakProxyForObject:self];
self.displayLink = [CADisplayLink displayLinkWithTarget:weakProxy selector:@selector(displayDidRefresh:)];
这样,self 的 dealloc 方法会被调用,在里面会调用 CADisplayLink 的 invalidate 方法,CADisplayLink 会被释放
- (void)dealloc
{
// Removes the display link from all run loop modes.
[_displayLink invalidate];
}
FLWeakProxy 对初始化时传入的 targetObject 进行弱引用,弱引用属性是 target。通过 Runtime 的消息转发机制 (参见 http://tech.glowing.com/cn/objective-c-runtime/) 把消息转发给 target,使 target 调用相应的方法。当 target 为空又收到消息时,把相应的方法返回值设置为空。具体代码如下
@interface FLWeakProxy ()
@property (nonatomic, weak) id target;
@end
@implementation FLWeakProxy
#pragma mark Life Cycle
// This is the designated creation method of an `FLWeakProxy` and
// as a subclass of `NSProxy` it doesn't respond to or need `-init`.
+ (instancetype)weakProxyForObject:(id)targetObject
{
FLWeakProxy *weakProxy = [FLWeakProxy alloc];
weakProxy.target = targetObject;
return weakProxy;
}
#pragma mark Forwarding Messages
- (id)forwardingTargetForSelector:(SEL)selector
{
// Keep it lightweight: access the ivar directly
return _target;
}
#pragma mark - NSWeakProxy Method Overrides
#pragma mark Handling Unimplemented Methods
- (void)forwardInvocation:(NSInvocation *)invocation
{
// Fallback for when target is nil. Don't do anything, just return 0/NULL/nil.
// The method signature we've received to get here is just a dummy to keep `doesNotRecognizeSelector:` from firing.
// We can't really handle struct return types here because we don't know the length.
void *nullPointer = NULL;
[invocation setReturnValue:&nullPointer];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
// We only get here if `forwardingTargetForSelector:` returns nil.
// In that case, our weak target has been reclaimed. Return a dummy method signature to keep `doesNotRecognizeSelector:` from firing.
// We'll emulate the Obj-c messaging nil behavior by setting the return value to nil in `forwardInvocation:`, but we'll assume that the return value is `sizeof(void *)`.
// Other libraries handle this situation by making use of a global method signature cache, but that seems heavier than necessary and has issues as well.
// See https://www.mikeash.com/pyblog/friday-qa-2010-02-26-futures.html and https://github.com/steipete/PSTDelegateProxy/issues/1 for examples of using a method signature cache.
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
@end
转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/7583289.html
NSTimer、CADisplayLink 内存泄漏的更多相关文章
- 浅析NSTimer & CADisplayLink内存泄露
偶得前言 NSRunLoop与定时器 - invalidate的作用 我们如何解决? 偶得前言 本篇文章中我们主要谈谈NSTimer\CADisplayLink在使用过程中牵扯到内存泄露的相关问题及解 ...
- 内存泄漏(I)
Block 解决内存泄漏 使用 weakSelf 进行解决 NSTimer 的内存泄漏与解决方案 内存泄漏
- 解决NSTimer存在的内存泄漏的问题
创建定时器会在一定的间隔后执行某些操作,一般大家会这样创建定时器,这样创建的定时,self对定时器有个引用,定时器对self也有个引用,造成了循环引用,最终造成了内存泄漏,如果定时器在做下载的操作就会 ...
- NSTimer内存泄漏导致控制器不调用dealloc
创建定时器会在一定的间隔后执行某些操作,一般大家会这样创建定时器,这样创建的定时,self对定时器有个引用,定时器对self也有个引用,造成了循环引用,最终造成了内存泄漏,如果定时器在做下载的操作就会 ...
- 利用RunTime解决由NSTimer导致的内存泄漏
NSTimer使用场景 用NSTimer来实现每隔一定时间执行制定的任务,例如最常见的广告轮播图,使用NSTimer实现这个功能很简单代码如下 NSTimer *_timer; _timer = [N ...
- NSTimer内存泄漏
用NSTimer调用 timer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:@selec ...
- NSTimer使用不当引发的内存泄漏问题
NSTimer可以用来执行一些定时任务,比较常用的方法就是: + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTar ...
- 利用NSProxy解决NSTimer内存泄漏问题
之前写过一篇利用RunTime解决由NSTimer导致的内存泄漏的文章,最近和同事讨论觉得这样写有点复杂,然后发现有NSProxy这么好用的根类,根类,根类,没错NSProxy与NSObject一样是 ...
- iOS内存泄漏自动检测工具PLeakSniffer
新款objective-C内存泄漏自动检测工具 PLeakSniffer , GitHub地址 (https://github.com/music4kid/PLeakSniffer). 背景 前些天读 ...
随机推荐
- 我的第一个spring boot程序(spring boot 学习笔记之二)
第一个spring boot程序 写在前面:鉴于spring注解以及springMVC的配置有大量细节和知识点,在学习理解之后,我们将直接进入spring boot的学习,在后续学习中用到注解及其他相 ...
- canvas粒子系统的构建
前面的话 本文将从最基本的imageData对象的理论知识说开去,详细介绍canvas粒子系统的构建 效果演示 下面是实例效果演示,博文结尾有全部源码 imageData 关于图像数据imageDat ...
- .NET Core 在程序集中集成Razor视图
前言 有时候,我们在开发一个程序集供其他项目引用的时候,可能需要对外输出一些HTML的结构数据. 还有一些情况我们可能开发的是一个中间件,这个中间件需要提供一些界面来对外展示数据或者是内部的一些程序的 ...
- InvalidateRect和UpdateWindow
The UpdateWindow function updates the client area of the specified window by sending a WM_PAINT mess ...
- 欠了好久的CRM帖子,双11来读。
又一年双11了,觉得天猫双11越来越没特色了. 从折扣,音符旋律到红包,今年15年却找不出往年的热度,只是商家还是一样的急,备着活动目标计划,做着库存价格打标视觉设计这种苦逼的日子. 欠了好久的CRM ...
- Python3调用企业微信用于告警
前段时间利用py爬虫抓取一些网页信息,然后通过wxpy发送到微信群,以用作日常告警,感觉还是很方便. 但好景不长,我的小号微信被腾讯封了(很常见咯), 显示无法登录网页版微信,至今已经有半个多月了. ...
- django日期比较
from django.db import models from django.utils import timezone import datetime # Create your models ...
- 网上搜索到的 比较好的mysql查询语句练习题
Sutdent表的定义 字段名 字段描述 数据类型 主键 外键 非空 唯一 自增 Id 学号 INT(10) 是 否 是 是 是 Name 姓名 VARCHAR(20) 否 否 是 否 否 Sex 性 ...
- java Properties类得到当前的系统属性
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt218 package com.shi.simpleUtil; import ...
- poj 1523求割点
题意:给出一个无向图,求割点以及去除这个点后图分为几部分: 思路:割点定义:去掉该点后图将分成几个部分.割点:(1)当k为根节点且有>1个分支,则去除该点后图便被分成几个分支.(2)DFN[v] ...