目标 c:使用 NSTimer 对象进行内存管理

Posted

技术标签:

【中文标题】目标 c:使用 NSTimer 对象进行内存管理【英文标题】:objective c: memory management with NSTimer objects 【发布时间】:2015-08-30 14:27:35 【问题描述】:

我是 Objective C 编程的新手,我来自 C++,并且会更好地了解 ARC 内存管理;如果我有以下情况:

-(void) test_method

 NSTimer* t=[NSTimer ScheduledTimerWithTimeInterval:2
 target:self  selector;@selector(exec_method) userinfo:nil repeats:YES];

在方法结束时,我预计会丢失引用 t,因此,对于 ARC,自动调用 release 并释放 NSTimer 对象,相反,它似乎仍在内存中(exec_method 重复它每 2 秒执行一次)..或者当系统需要内存空间时它会被释放?

【问题讨论】:

【参考方案1】:

您确实正确理解了 ARC - 这只是一个不太明显的情况,因为您看不到对您的对象的额外强引用。 NSTimer 的行为不符合预期,因为它被安排在运行循环上,这意味着它也被保留在那里。因此,当您的本地人离开时,该对象仍保留在内存中。

ARC 底层使用引用计数系统 - 每个对象都有一个分配给它的数字(称为保留计数),并且只有当该数字达到零时才会释放对象。当使用alloccopynew 创建对象时,保留计数设置为 1。当对象被另一个对象保留时,数目增加,当它被释放时减少(在 pre- ARC MRR 系统这些是程序员进行的实际方法调用 - retainrelease)。 ARC 以相同的方式工作,但只是在编译时自动添加相同的调用)。

因此,在这种情况下,ARC 生成的对 release 的隐式调用只是将计数从 2 减少 1,但由于它没有达到零,因此不会释放对象。使计时器无效会将其从运行循环中移除,并导致其被释放。

【讨论】:

【参考方案2】:

来自 Apple 文档:

定时器与运行循环一起工作。为了有效地使用计时器, 你应该知道运行循环是如何运作的——参见 NSRunLoop 和 线程编程指南。特别注意运行循环 保持对他们的计时器的强烈引用,所以你不必 添加计时器后,保持您自己对计时器的强引用 到一个运行循环。

您必须使 NSTimer 无效才能将其从运行循环中移除。

https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/

为了简化这个过程,你可以做的是创建两种方法,一种是创建和启动计时器,另一种是使时间无效。这些方法需要您将时间声明为 IVAR。

斯威夫特:

let timer = NSTimer(timeInterval: 1.0, target: self, selector: "incrementCompletedUnitCount:",
    userInfo: nil, repeats: true)

progress.cancellationHandler = 
    timer.invalidate()


progress.cancel()

目标-C

NSTimer * _studentTimer1;

-(void)startStudentTimer 
    NSLog(@"***TIMER STARTED***");
    _studentTimer1 = [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(dowork) userInfo:nil repeats:TRUE];


-(void)invalidateStudentTimer1 
    [_studentTimer1 invalidate];

另外,为了安全起见,您可能希望将您的失效方法放在视图控制器的 dealloc 方法中。

您还可以考虑通过使用指向计时器的弱指针来采取额外的安全措施,如下所示:

NSTimer* __weak timer = [NSTimer scheduledTimerWithTimeInterval:30.0f target: self selector:@selector(tick) userInfo:nil repeats:YES];

或作为 IVAR:

NSTimer * __weak _studentTimer1;

不,至于你的最后一个问题,时间会一直停留在运行循环中,直到你明确地将其无效,这就是为什么你需要小心使用 NSTimer 并且应该尽可能安全地将其包装起来。

【讨论】:

以上是关于目标 c:使用 NSTimer 对象进行内存管理的主要内容,如果未能解决你的问题,请参考以下文章

iOS 6 中的 NSTimer 内存管理

[crash详解与防护] NSTimer crash

第52条:别忘了NSTimer会保留其目标对象

iOS之深入解析内存管理NSTimer的强引用问题

目标 C - 因保留周期而丢失

布尔包装?制作一个 `BOOL` 值的对象。 (目标-C)