我以60 fps的速度NSTimer
开火。它更新了C ++模型,然后通过Quartz 2D绘制。这很有效,除非内存快速累积,即使我没有分配任何东西。仪器报告没有泄漏,但很多CFRunLoopTimers
(我想从重复NSTimer
?)似乎正在积累。单击窗口或按一个键清除它们中的大多数,这似乎指向自动释放池没有经常耗尽。我是否必须依靠事件来循环自动释放池,或者是否有更好的方法来清除内存?
感谢任何帮助,谢谢
萨姆
创建计时器(timer
是一个ivar):
timer = [NSTimer scheduledTimerWithTimeInterval:1.0f / 60 target:self selector:@selector(update:) userInfo:nil repeats:YES];
update:
方法:
- (void)update:(NSTimer *)timer {
controller->Update();
[self.view setNeedsDisplay:YES];
}
更新
在对此进行了一些讨论之后,我又做了一些额外的观察。
1。)[self.view setNeedsDisplay:YES]
似乎是产生这些CFRunLoopTimers
的罪魁祸首。用[self.view display]
替换它可以解决问题,但代价是性能。
2.。)将频率降低到20-30 fps并保持“[self.view setNeedsDisplay:YES]”也会导致问题消失。
这似乎意味着setNeedsDisplay:
不喜欢被大量调用(可能会显示每秒更多的时间?)。我坦率地无法理解“过度调用”它的问题,如果所有这一切都告诉视图在eventloop结束时重新显示。
我相信我在这里遗漏了一些东西,非常感谢任何额外的帮助。
答案 0 :(得分:6)
通常,正确的解决方案是围绕对象创建繁重的代码创建嵌套的NSAutoreleasePool。
但是在这种情况下,当计时器重新安排自己时,似乎对象是自动释放的 - 一段你无法控制的代码。并且你不能要求最顶层的自动释放池在不释放的情况下自行排出。
在您的情况下,解决方案是放弃NSTimer以进行帧速率同步,并改为使用CADisplayLink
:
CADisplayLink *frameLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(update:)];
// Notify the application at the refresh rate of the display (60 Hz)
frameLink.frameInterval = 1;
[frameLink addToRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
CADisplayLink用于将绘图与屏幕的刷新率同步 - 因此它似乎是您想要做的事情的一个很好的候选者。此外,当以60 Hz运行时,NSTimer的精度不足以与显示器刷新率同步。
答案 1 :(得分:1)
好吧,不管内存清理问题如何:
NSTimer的文档说:“由于典型的运行循环管理各种输入源,定时器的时间间隔的有效分辨率限制在50-100毫秒的数量级。”
1/60是大约的间隔。 16.6毫秒,所以你远远超出了NSTimer的有效分辨率。
您的后续注释表明将其频率降低到20-30 fps可将其固定... 20 fps将间隔时间设置为50 ms - 在记录的分辨率范围内。
文档还表明这不应该破坏任何东西......但是,我遇到了一些奇怪的情况,仪器引起了之前没有的内存问题。在没有安装Xcode或Instruments的情况下,您是否在Release版本中运行应用程序时出现内存问题/警告?
我想在这一点上我建议继续前进并尝试其他发布的答案中的工具。
答案 2 :(得分:0)
正如Kemenaran
已经建议的那样,我也认为您应该尝试使用CADisplayLink
对象。另一个原因是NSTimer的射击限制为50-100毫秒(source):
由于典型的运行循环管理各种输入源,因此定时器的时间间隔的有效分辨率限制在50-100毫秒的数量级。
另一方面,我不确定这会解决问题。根据您的描述,在我看来,事情可以像这样(或以类似的方式):
当您执行[self.view setNeedsDisplay:YES];
时,框架会通过CFRunLoopTimer
计划重绘视图;这解释了为什么有这么多人被创造出来;
当CFRunLoopTimer
触发时,视图重新绘制,needsDisplay
标记重置;
在你的情况下,当update
的频率很高时,你会更频繁地调用setNeedsDisplay
而不是实际发生刷新;因此,对于每次实际刷新,您都会多次调用setNeedsDisplay
,并且还会创建多个CFRunLoopTimer
;
在两个连续的实际刷新操作之间创建的所有CFRunLoopTimer
,只有第一个被释放和销毁;其他人没有机会发射或找到needsDisplay
标志已经重置的视图,因此可能会自行重新安排。
对于第4点:我认为最可能的解释是第一个:你建立一个CFRunLoopTimer
s的队列,其频率远高于你“消耗”它的频率。我说重绘时间比update
周期要长,因为你说当你调用[view display]
时性能会受到影响。
如果这是正确的,那么问题也会随着CADisplayLink而持续存在(因为它与调用更新的频率与重绘速度相比更高)并且唯一的解决方案是找到一种不同的方式来重绘(即,不使用setNeedsDisplay:YES
)
事实上,我使用cocos2d源检查了setNeedsDisplay:YES
几乎没有使用过。重绘(cocos2d提供60 fps的帧速率)是通过直接绘制到OpenGL缓冲区来完成的,我怀疑这是能够达到该帧速率的关键点。您还可以检查是否可以用CAEAGLLayer
替换视图图层(这应该非常简单)并查看是否可以直接绘制到glBuffer
。
我希望它有所帮助。请记住,我在这里做了许多假设,所以很可能是任何错误。我只是在提出我的推理。