60 Hz NSTimer and auto-implemented memory

I have NSTimer shooting at 60 frames per second. It updates the C ++ model and then draws through Quartz 2D. This works well, but memory builds up quickly, although I don't allocate anything. There are no leaks in the devices, but many CFRunLoopTimers (I think from a repeating NSTimer ?) NSTimer accumulate. Clicking on a window or pressing a key clears most of them, which, apparently, indicates that the auto-advertisement pool does not merge often enough. Should I rely on events to quote the auto-resource pool, or is there a better way to clear the memory?

Any help is appreciated, thanks

-Sam

Creating a timer ( timer - ivar):

 timer = [NSTimer scheduledTimerWithTimeInterval:1.0f / 60 target:self selector:@selector(update:) userInfo:nil repeats:YES]; 

update: method:

 - (void)update:(NSTimer *)timer { controller->Update(); [self.view setNeedsDisplay:YES]; } 

Update:

After I got into it a bit, I made a few extra observations.

1.) [self.view setNeedsDisplay:YES] seems to be the culprit in the appearance of these CFRunLoopTimers . Replacing it with [self.view display] fixes the problem, but at the expense of performance.

2.) Reducing the frequency to 20-30 frames per second and saving `[self.view setNeedsDisplay: YES] 'also causes the problem to go away.

This seems to imply that setNeedsDisplay: doesn't want to be called much (maybe more time per second can be displayed?). Frankly, I can’t understand that the problem with "relocation", if all this does, is to show that the view will be redrawn at the end of the eventloop.

I am sure that something is missing here, and any additional help is appreciated.

+6
source share
3 answers

Normally, the right solution would be to create a nested NSAutoreleasePool around your heavy code with creations.

But in this case, it seems that the objects are being implemented when the timer redistributes itself - part of the code that you cannot control. And you cannot ask that the topmost pool of auto resources could merge itself without releasing it.

In your case, the solution would be to abandon NSTimer for frame synchronization and use 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 made the drawing synchronize with the refresh rate of the screen - so it seems like a good candidate for what you want to do. In addition, NSTimer is not accurate enough to synchronize with the display refresh rate when operating at 60 Hz.

+5
source

Well, regardless of the problem with clearing the memory:

The documentation for NSTimer says: "Because of the various input sources, a typical startup cycle is controlled, the effective resolution of the time interval for the timer is limited to about 50-100 milliseconds."

1/60 - interval approx. 16.6 milliseconds, so you far exceed the effective resolution of NSTimer.

Your note about the next steps indicates that reducing its frequency to 20-30 frames per second corrects this ... 20 frames per second provides an interval of up to 50 ms - within the limits of the documented resolution.

The documentation also indicates that this will not break anything ... however, I ran into some odd situations where the tools caused memory problems that were not previously. Are you getting memory issues / warnings that start the application in the Release assembly, without Xcode or Instruments binding?

I assume that at this point I would recommend just going over and testing the tools in the other posted answers.

+1
source

As Kemenaran has already suggested, I also think that you should try and use the CADisplayLink object. Another reason is that NSTimer has a lower fire limit of 50-100 milliseconds ( source ):

Due to various input sources, a typical trigger cycle is controlled, the effective resolution of the time interval for the timer is limited to about 50-100 milliseconds.

On the other hand, I am not sure if this will solve the problem. From what you described, it seems to me that this thing can work this way (or similarly):

  • when you execute [self.view setNeedsDisplay:YES]; framework, redraw the view using CFRunLoopTimer ; this explains why so much is being created;

  • when CFRunLoopTimer triggered, the image is redrawn, needsDisplay flag reset;

  • in your case, when the update frequency is high, what happens is that you call setNeedsDisplay more often than the update can happen; therefore for each actual update you have several calls to setNeedsDisplay , as well as several CFRunLoopTimer ;

  • of all those CFRunLoopTimer that are created between two consecutive valid update operations, only the first is freed and destroyed; others either do not have the ability to start or find a view with the needsDisplay flag already reset and, therefore, can transfer themselves.

In paragraph 4: I think that the most likely explication is the first: you create a CFRunLoopTimer at a frequency much higher than the one at which you can "consume" it. I say redrawing takes longer than the update cycle because you say that when you call [view display] performance suffers.

If this is correct, the problem will persist with CADisplayLink (because it involves calling the update at a too high frequency compared to the redraw speed), and the only solution would be to find another way to redraw (i.e., without using setNeedsDisplay:YES )

In fact, I checked with cocos2d and setNeedsDisplay:YES almost never used. Redrawing (cocos2d provides a frame rate of 60 frames per second) is done by directly drawing to the OpenGL buffer, and I suspect that this is a critical point to be able to achieve this frame rate. You can also check if you can replace the view layer with CAEAGLLayer (this should be pretty easy) and see if you can draw directly in glBuffer .

Hope this helps. Keep in mind that there are many hypotheses that I make here, so it may well be that any of them is wrong. I just offer my reasoning.

0
source

Source: https://habr.com/ru/post/892716/


All Articles