2009-08-18 4 views
2

Ich habe eine UIImage, die ich mit imageWithData instanziiere: (Die Daten werden mit [NSData dataWithContentsOfFile:] aus dem Bundle geladen).Speicherverwaltung beim Zeichnen auf eine Ansicht auf dem iPhone

bin Zeichnung ich dann das Bild wie folgt:

NSData *imageData = [NSData dataWithContentsOfFile:fileLocation]; 
UIImage *myImage = [UIImage imageWithData:imageData]; 

//These lines are superfluous from what I can tell, replacing with 
//UIImage *myImage = [UIImage imagedNamed:imageName]; very soon. 

[myImage drawAtPoint:CGPointMake(0,0)]; 
//myImage will be released at the end of the run loop 

Meine Frage lautet: Die UIImage erstellt Autoreleased. Was passiert in Bezug auf Speicher, wenn die UIImage auf die Ansicht gezeichnet wird und dann die UIImage nicht mehr existiert. Offensichtlich ist das Bild visuell immer noch da, wie es in einen Kontext gezeichnet wurde.

Wird die Speicherbelegung verdoppelt, wenn die UIImage gültig ist UND in die Ansicht gezogen wurde, und dann zur gleichen Menge zurückkehren, als ob nur ein UIImage existierte, nachdem die UIImage freigegeben wurde?


Jetzt gehen Sie eine andere Route. Wenn ich [UIImage imageNamed:] verwendet habe, um das Bild zu instantiieren, dann hat die UIImage-Klasse ihren eigenen Bild-Cache und enthält immer nur eine echte Instanz eines bestimmten Bildes (unabhängig davon, wie viele UIImage-Instanzen erstellt werden) repräsentieren dieses eine Bild).

Meine andere Frage ist: Was passiert mit dem Bild im Cache, wenn ich es in einen Kontext zeichnen und dann das UIimage freigegeben wird (über Autorelease am Ende der Laufschleife)? Bleibt das Bild im Cache und verbraucht es Speicher? Wird es entfernt, weil keine anderen UIImage-Instanzen es verwenden?

Jede Hilfe wäre großartig, danke!

+0

Könnten Sie bitte einen gründlicheren Code zur Verfügung stellen? –

+0

Es gibt nicht viel mehr Code zur Verfügung, wenn ich ehrlich bin. Ich füge hinzu, was nicht da ist. – Jasarien

Antwort

1

Ich empfehle wirklich mit einfachen Code hier zu starten und dann zu optimieren, wo Sie tatsächlich Probleme haben. Die Art von Raumoptimierungen, die Sie beschreiben, wird wahrscheinlich zu ernsthaften Zeitproblemen führen (das erneute Erzeugen der UII-Grafik in -drawRect: zum Beispiel ist sehr teuer). Das heißt, schauen wir uns durch die verschiedenen Fragen:

  • Erstens, wie ich sagte, mit dem Tun teure Arbeit in -drawRect: vorsichtig sein. Sie haben wenig Kontrolle darüber, wie oft oder wann es aufgerufen wird, und jede teure Arbeit hier (z. B. das Erstellen eines neuen UIImage, insbesondere wenn Sie es von der Festplatte lesen müssen) kann die Leistung der Benutzeroberfläche erheblich beeinträchtigen.

  • Ich gehe davon aus, dass es viel mehr zu Ihrem -drawRect: dann ist das, richtig? UIImageView ist optimiert für das, was Sie hier getan haben, sowohl in der Geschwindigkeit als auch im Speicher.Aber wenn Ihre Ansicht viel komplizierter ist, dann ist das Zeichnen des Bildes selbst besser als das Erstellen vieler Unteransichten.

  • Wie bereits erwähnt, wenn Sie -drawAtPoint: aufrufen, ist die Kopie, die Sie erstellen, eine Bitmap-Darstellung (gemischt mit jeder anderen Zeichnung, die erstellt wird). Es hat nichts mit der ursprünglichen UIImage in Bezug auf die Speichernutzung zu tun. Du kannst keinen für den anderen tauschen. Der für die Bitmap-Darstellung erforderliche Speicher hängt von der Größe und Bittiefe der Ansicht ab, und Sie können ihn nicht ändern. Es ist egal, ob das UII-Bild existiert, nachdem es gezeichnet wurde.

  • -imageNamed: tut Caching für Sie und ist in der Regel eine gute Wahl. Beachten Sie, dass der Cache in Situationen mit wenig Arbeitsspeicher nicht gelöscht wird. Die UIImages selbst löschen ihre zugrundeliegenden Daten transparent, wenn sie aus einer Datei geladen wurden (und sie werfen auf jeden Fall zusätzliche Darstellungen ab). Die UIImage-Referenz enthält Informationen darüber, wie dies geschieht.

  • Wenn Sie sehr um die Leistung dieser Bilder (Raum oder Zeit) besorgt sind und Sie nicht die Funktionalität von UIImage benötigen, sollten Sie mit CGImages arbeiten. Sie sind nicht so flexibel wie UIImagages, und sie sind komplexer zu programmieren, aber sie sind effizienter. Das heißt, UIImagages sind für die meisten Zwecke gut.

+0

UIImageView ist nicht optimiert, es ist einfach nur praktisch. Das gleiche gilt für UILabel. – rpetrich

+0

Es behauptet, es ist (siehe die Subclassing Notes). Es umgeht drawRect :. Habe nicht gegraben, was es stattdessen tut, aber sie behaupten, etwas Phantasievolles zu tun. –

1

Zu jedem Zeitpunkt, wenn Sie "in den Kontext zeichnen", enthält der Kontext keinen Verweis auf das Bild. Stattdessen werden beim Zeichnen die Pixel im Kontext platziert, so dass der Verweis auf UIImage (oder etwas, das darauf gezeichnet wird - NSString, NSPath usw.) nicht benötigt wird.

In Bezug auf imageNamed:, wird es nie wirklich veröffentlicht werden - was Sie erhalten, ist ein Verweis, den Sie nicht (und sollten nicht) freigeben, aber das im Cache gespeicherte Bild könnte noch da sein.

+0

Ich werde es nicht selbst veröffentlichen. [UIImage imageNamed:] gibt eine autoreleased UIImage-Instanz zurück. Es wird sich am Ende der Laufschleife selbst lösen. Was ich wissen möchte, ist, wenn das zwischengespeicherte Bild freigegeben wird, wenn irgendwelche UIImagages, die darauf verweisen, freigegeben werden? – Jasarien

+0

Die Spezifikation sagt nicht, ob es die zwischengespeicherten Bilder freigibt oder nicht. Ich würde annehmen, dass es nicht so ist. Dies könnte im System-Äquivalent von 'didReceiveMemoryWarning' geschehen, aber wiederum: nicht nach meinem Wissen. –

+0

imageNamed: Bilder werden im Cache gespeichert, bis das System-Äquivalent von didReceiveMemoryWarning, aber nur bei 3.0 - auf 2.x, ein Fehler existiert, wo der Cache nie gelöscht werden würde. – rpetrich

0

Es hängt wirklich davon ab, wo Sie die UIImage-Instanz selbst erstellen. Wenn dies eine UIView-Unterklasse ist, erstellen Sie die UIImage in -drawRect: oder -initWithFrame: oder woanders? Wenn Sie das Bild einmal laden (in Ihrem init oder mit einem Setter, der sich behält, usw.), sollte es kein Problem geben. Wenn Sie das Bild jedoch immer wieder in -drawRect: erstellen, haben Sie zumindest ein Performance-Problem.

+0

Das Hauptproblem, das ich hier habe, ist, dass ich ein Bild nicht behalten möchte, das ich bis drawRect nicht verwenden werde. Die Ansicht wird nicht sehr oft neu gezeichnet, daher ist es nicht so problematisch, das Bild innerhalb von drawRect zu instanziieren. Wenn ich es in Init instantiiere, dann verbraucht das Bild Speicher, auch wenn er nicht benutzt wird - was für mich keinen Sinn ergibt. – Jasarien

+0

Dann würde ich mit '[UIImage imageNamed:]' gehen, weil, wie Sie sagten, es das Bild zwischenspeichert, aber könnte den Cache leeren, wenn Speicher knapp wird. Auf diese Weise haben Sie immer noch Ihre Referenz, aber die eigentlichen Daten werden möglicherweise gelöscht. UIImage kümmert sich um all die harte Arbeit. – jbrennan