2017-02-16 11 views
0

Meine macOS-Anwendung leidet unter ernsthaften Speicherlecks. Nach vielem Debuggen scheint ich die Ursache gefunden zu haben, obwohl ich es immer noch nicht wirklich verstehe.macOS: Speicherverbrauch verursacht durch setNeedsDisplayInRect

Die App zeichnet Pixel in einen internen Puffer, der dann tatsächlich in der drawRect-Methode einer NSView-Unterklasse zum Bildschirm gezeichnet wird. Das Repaint wird von einem [self setNeedsDisplayInRect:rect] ausgelöst.

Nach ein paar Stunden verbraucht das Programm mehrere Gigabyte RAM. Aber wenn ich den Anruf auf setNeedsDisplayInRect in einer Schleife setze, sagen wir, 10.000 Mal statt nur einmal, wann immer ich neu streichen will, steigt der Speicherverbrauch in wenigen Minuten auf Gigabyte.

Um sicherzustellen, dass der Speicher in der drawRect Methode nicht leckt, habe ich den vollständigen Körper entfernt, so dass nur eine leere Methode übrig bleibt.

Soweit ich aus der Dokumentation verstehe, werden alle an setNeedsDisplayInRect vergebenen Exemplare gespeichert, bis das Repaint im nächsten Durchlauf der Ereignisschleife tatsächlich verarbeitet wird. Wenn ich es also 10.000 Mal anrufe, erwarte ich, dass der Speicherverbrauch höher ist, aber ich erwarte nicht, dass es ständig weiter steigt - ich erwarte, dass alle gespeicherten Rekruten nach einer Iteration der Ereignisschleife bereinigt werden.

Also, warum steigt die Menge an Speicher weiter auf setNeedsDisplayInRect? Ich weiß, es 10.000 mal pro Sekunde zu nennen ist nicht gerade normal, aber die Speicherauslastung steigt auch in normalen Situationen weiter an, obwohl viel langsamer.

Codeauszug:

- (void)drawRect:(NSRect)dirtyRect { 
// Currently, nothing happening 
} 

// My own method, that gets called from elsewhere. 
- (void) drawToScreen:(int) x : (int) y : (int)w :(int)h :(int *)data 
{  
    int rectYPos = MAX(height - y - h, 0); 
    NSRect rect = NSMakeRect(x, rectYPos, w, h); 
    for(int i = 0; i < 10000; i++) 
    { 
     [self setNeedsDisplayInRect:rect]; 
    } 
} 

Antwort

1

Wenn Sie diesen Thread von einem sekundären/Hintergrund-Aufruf sind, könnte man (von der Apple-Docs) überprüfen möchten:

„Wenn ein sekundärer Thread einer Anwendung möchte, dass Teile der Ansicht im Hauptthread neu gezeichnet werden. Dies darf nicht mit Methoden wie display, setNeedsDisplay :, setNeedsDisplayInRect: oder setViewsNeedDisplay: geschehen. Stattdessen sollte eine Nachricht an den Hauptthread gesendet oder diese Methoden aufgerufen werden Verwenden Sie stattdessen die Methode performSelectorOnMainThread: withObject: waitUntilDone:. "

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html

+0

Dies schien tatsächlich das Problem zu sein. Seltsam, dass dies in den setNeedsDisplay * -Dokumenten nicht dokumentiert ist. Ich habe die '[self setNeedsDisplayInRect: rect]' dadurch ersetzt: 'dispatch_get_main_queue(),^(void) {[self setNeedsDisplayInRect: rect];});' ... und jetzt leckt der Speicher nicht mehr. Das heißt, es steigt immer noch langsam auf, fällt aber nach einer Weile plötzlich ab; wiederholen. – Mathijs

Verwandte Themen