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 内存泄漏的更多相关文章

  1. 浅析NSTimer & CADisplayLink内存泄露

    偶得前言 NSRunLoop与定时器 - invalidate的作用 我们如何解决? 偶得前言 本篇文章中我们主要谈谈NSTimer\CADisplayLink在使用过程中牵扯到内存泄露的相关问题及解 ...

  2. 内存泄漏(I)

    Block 解决内存泄漏 使用 weakSelf 进行解决 NSTimer 的内存泄漏与解决方案 内存泄漏

  3. 解决NSTimer存在的内存泄漏的问题

    创建定时器会在一定的间隔后执行某些操作,一般大家会这样创建定时器,这样创建的定时,self对定时器有个引用,定时器对self也有个引用,造成了循环引用,最终造成了内存泄漏,如果定时器在做下载的操作就会 ...

  4. NSTimer内存泄漏导致控制器不调用dealloc

    创建定时器会在一定的间隔后执行某些操作,一般大家会这样创建定时器,这样创建的定时,self对定时器有个引用,定时器对self也有个引用,造成了循环引用,最终造成了内存泄漏,如果定时器在做下载的操作就会 ...

  5. 利用RunTime解决由NSTimer导致的内存泄漏

    NSTimer使用场景 用NSTimer来实现每隔一定时间执行制定的任务,例如最常见的广告轮播图,使用NSTimer实现这个功能很简单代码如下 NSTimer *_timer; _timer = [N ...

  6. NSTimer内存泄漏

    用NSTimer调用 timer = [NSTimer scheduledTimerWithTimeInterval:timerInterval target:self selector:@selec ...

  7. NSTimer使用不当引发的内存泄漏问题

    NSTimer可以用来执行一些定时任务,比较常用的方法就是: + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTar ...

  8. 利用NSProxy解决NSTimer内存泄漏问题

    之前写过一篇利用RunTime解决由NSTimer导致的内存泄漏的文章,最近和同事讨论觉得这样写有点复杂,然后发现有NSProxy这么好用的根类,根类,根类,没错NSProxy与NSObject一样是 ...

  9. iOS内存泄漏自动检测工具PLeakSniffer

    新款objective-C内存泄漏自动检测工具 PLeakSniffer , GitHub地址 (https://github.com/music4kid/PLeakSniffer). 背景 前些天读 ...

随机推荐

  1. python基础入门(1)

    1.python环境安装 1.1 windows安装 打开官网 https://www.python.org/downloads/windows/ 下载中心 安装过程和普通应用过程一样,如果是pyth ...

  2. Redis持久化总结

    Redis持久化总结 因为Redis是内存型数据库,所以为了防止因为系统崩溃等原因导致数据丢失的问题,Redis提供了两种不同的持久化方法来将数据存储在硬盘里面,一种方法是快照(RDB),它可以将存在 ...

  3. Requests抓取有道翻译结果

    Requests比urllib更加方便,抓取有道翻译非常的简单. import requests class YouDao():     def __init__(self,parm):        ...

  4. java --jfree报表

    ---恢复内容开始--- 1.使用的报表工具: jfree报表 2.下载网址: http://www.jfree.org/ 下载之后先解压:如下图 下载后:需要的jar包!如下图: 打开:找到以下的两 ...

  5. Django连接mysql数据库

    1.app中对应的models.py配置相关表结构信息 from django.db import models class Question(models.Model): question_text ...

  6. 【 DCOS 】织云 CMDB 管理引擎技术详解

    欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~ 作者 : 李琦 , 腾讯高级工程师 , 就职于网络平台部.曾负责公司海量运营系统的规划设计,如 TMP.Sniper.GSLB.IDCSp ...

  7. Javascript用数组实现栈和队列

    栈是遵循后进先出(LIFO)规则的一种有序集合,比如桌上的一叠书,我们只能从上面放或取. 队列是遵循先进先出(FIFO)规则的一种有序集合,比如排队,先排到的先离开. 数组也是一种有序的集合,它与上面 ...

  8. Webservice优缺点总结

    优点: 1 .采用xml支持跨平台远程调用. 2.基于http的soap协议,可跨越防火墙. (因为SOAP一般使用HTTP协议,而服务器的这个协议一般都是开放的,而且是可以穿过防火墙的) 3.支持面 ...

  9. tomcat 和 jboss access log 日志输出详解

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt179 工作中nginx+jboss/tomcat反向代理集成,想打开后端jb ...

  10. WebServices 之 WSDL

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt234 一,WSDL概述 WebServices Description La ...