Die Nutzung des Speichers ist ein Aspekt der Entwicklung, bei dem Sie wirklich vorsichtig sein müssen. Andernfalls kann Ihre App langsamer werden, viel Speicherplatz beanspruchen oder sogar alles abstürzen. Dieses Tutorial hilft Ihnen dabei, diese möglichen negativen Ergebnisse zu vermeiden!
Werfen wir einen Blick auf das Endergebnis, auf das wir hinarbeiten:
Klicken Sie auf eine beliebige Stelle der Bühne, um einen Feuerwerkseffekt zu erstellen, und behalten Sie den Speicherprofiler in der oberen linken Ecke im Auge.
Wenn Sie Ihre Anwendung jemals mit einem Profilerstellungstool profiliert haben oder einen Code oder eine Bibliothek verwendet haben, der Sie über die aktuelle Speicherbelegung Ihrer Anwendung informiert, haben Sie möglicherweise festgestellt, dass die Speicherbelegung oft ansteigt und dann wieder abnimmt (wenn Sie einen Port verwenden.) Ihr Code ist hervorragend!). Obwohl diese durch eine hohe Speicherauslastung hervorgerufenen Spitzen etwas cool aussehen, sind dies weder für Ihre Anwendung noch für Ihre Benutzer eine gute Nachricht. Lesen Sie weiter, um zu verstehen, warum dies geschieht und wie Sie es vermeiden können.
Das Bild unten ist ein wirklich gutes Beispiel für eine schlechte Speicherverwaltung. Es ist von einem Prototyp eines Spiels. Sie müssen zwei wichtige Dinge beachten: die großen Spitzen der Speicherauslastung und die Auslastung der Speicherauslastung. Der Gipfel liegt fast bei 540 MB! Das bedeutet, dass dieser Prototyp allein 540 MB des Arbeitsspeichers des Benutzers erreicht hat - und das möchten Sie unbedingt vermeiden.
Dieses Problem beginnt, wenn Sie mit der Erstellung vieler Objektinstanzen in Ihrer Anwendung beginnen. Nicht verwendete Instanzen verwenden den Speicher Ihrer Anwendung so lange, bis der Garbage Collector ausgeführt wird, wenn sie freigegeben werden. Dies verursacht die großen Spitzen. Eine noch schlimmere Situation tritt auf, wenn die Instanzen einfach nicht freigegeben werden, was dazu führt, dass die Speichernutzung Ihrer Anwendung so lange ansteigt, bis etwas abstürzt oder abbricht. Wenn Sie mehr über das letztere Problem und seine Vermeidung erfahren möchten, lesen Sie diesen Quick Tip über die Garbage Collection.
In diesem Lernprogramm werden keine Probleme mit dem Garbage-Collector behandelt. Stattdessen arbeiten wir daran, Strukturen zu erstellen, die Objekte effizient im Speicher halten, die Verwendung vollständig stabilisieren und verhindern, dass der Speicherbereiniger den Speicher bereinigt, wodurch die Anwendung schneller wird. Sehen Sie sich die Speicherauslastung desselben Prototyps an, diesmal jedoch mit den hier gezeigten Techniken:
Alle diese Verbesserungen können durch Objekt-Pooling erreicht werden. Lesen Sie weiter, um zu verstehen, was es ist und wie es funktioniert.
Objekt-Pooling ist eine Technik, bei der eine vordefinierte Anzahl von Objekten erstellt wird, wenn die Anwendung initialisiert wird und während der gesamten Anwendungslebensdauer im Speicher verbleibt. Der Objektpool gibt Objekte an, wenn die Anwendung sie anfordert, und setzt die Objekte auf den ursprünglichen Zustand zurück, wenn die Anwendung sie vollständig verwendet hat. Es gibt viele Arten von Objektpools, aber wir werden nur zwei davon betrachten: den statischen und den dynamischen Objektpool.
Der statische Objektpool erstellt eine definierte Anzahl von Objekten und behält nur diese Anzahl von Objekten während der gesamten Anwendungslebensdauer bei. Wenn ein Objekt angefordert wird, der Pool jedoch bereits alle Objekte angegeben hat, gibt der Pool den Wert null zurück. Wenn Sie diese Art von Pool verwenden, müssen Sie Probleme wie das Anfordern eines Objekts und das Zurückholen von nichts angehen.
Der dynamische Objektpool erstellt auch eine definierte Anzahl von Objekten bei der Initialisierung. Wenn jedoch ein Objekt angefordert wird und der Pool leer ist, erstellt der Pool automatisch eine andere Instanz, gibt dieses Objekt zurück, erhöht die Poolgröße und fügt das neue Objekt hinzu.
In diesem Lernprogramm erstellen wir eine einfache Anwendung, die Partikel generiert, wenn der Benutzer auf den Bildschirm klickt. Diese Partikel haben eine begrenzte Lebensdauer und werden dann vom Bildschirm entfernt und in den Pool zurückgegeben. Zu diesem Zweck erstellen wir diese Anwendung zunächst ohne Objektpooling und überprüfen die Speicherbelegung. Anschließend implementieren Sie den Objektpool und vergleichen die Speicherbelegung mit der vorherigen.
Öffnen Sie FlashDevelop (siehe dieses Handbuch) und erstellen Sie ein neues AS3-Projekt. Wir verwenden ein einfaches kleines farbiges Quadrat als Partikelbild, das mit Code gezeichnet wird und sich in einem zufälligen Winkel bewegen wird. Erstellen Sie eine neue Klasse namens Particle, die Sprite erweitert. Ich gehe davon aus, dass Sie mit der Erzeugung eines Partikels umgehen können, und markieren Sie einfach die Aspekte, die die Lebensdauer und die Entfernung des Partikels verfolgen. Wenn Sie Probleme beim Erstellen des Partikels haben, können Sie den vollständigen Quellcode dieses Tutorials oben auf der Seite herunterladen.
private var _lifeTime: int; Aktualisierung der öffentlichen Funktion (timePassed: uint): void // Bewegung des Teilchens x + = Math.cos (_angle) * _speed * timePassed / 1000; y + = Math.sin (_angle) * _speed * timePassed / 1000; // Kleine Erleichterung, damit Bewegung schön aussieht _speed - = 120 * timePassed / 1000; // Pflege der Lebensdauer und Entfernung _lifeTime - = timePassed; if (_lifeTime <= 0) parent.removeChild(this);
Der obige Code ist der Code, der für das Entfernen des Partikels vom Bildschirm verantwortlich ist. Wir erstellen eine Variable namens _Lebenszeit
Die Anzahl der Millisekunden, die das Partikel auf dem Bildschirm anzeigen soll. Wir initialisieren standardmäßig den Wert 1000 im Konstruktor. Das aktualisieren()
Die Funktion wird bei jedem Frame aufgerufen und empfängt die Anzahl der Millisekunden, die zwischen den Frames vergangen sind, sodass der Lebensdauerwert des Partikels verringert werden kann. Wenn dieser Wert 0 oder weniger erreicht, fordert das Partikel automatisch das übergeordnete Element auf, es vom Bildschirm zu entfernen. Der Rest des Codes sorgt für die Bewegung der Partikel.
Jetzt lassen wir eine Reihe von diesen erstellen, wenn ein Mausklick erkannt wird. Gehe zu Main.as:
private var _oldTime: uint; private var _elapsed: uint; private Funktion init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); // Einstiegspunkt stage.addEventListener (MouseEvent.CLICK, createParticles); addEventListener (Event.ENTER_FRAME, updateParticles); _oldTime = getTimer (); private function updateParticles (e: Event): void _elapsed = getTimer () - _oldTime; _oldTime + = _elapsed; für (var i: int = 0; i < numChildren; i++) if (getChildAt(i) is Particle) Particle(getChildAt(i)).update(_elapsed); private function createParticles(e:MouseEvent):void for (var i:int = 0; i < 10; i++) addChild(new Particle(stage.mouseX, stage.mouseY));
Der Code zum Aktualisieren der Partikel sollte Ihnen bekannt sein: Er ist der Ursprung einer einfachen zeitbasierten Schleife, die üblicherweise in Spielen verwendet wird. Vergessen Sie nicht die Import-Anweisungen:
import flash.events.Event; import flash.events.MouseEvent; import flash.utils.getTimer;
Sie können Ihre Anwendung jetzt mit dem integrierten Profiler von FlashDevelop testen und profilieren. Klicken Sie einige Male auf dem Bildschirm. So sah meine Speichernutzung aus:
Ich klickte, bis der Garbage Collector gestartet wurde. Die Anwendung erstellte über 2000 Partikel, die gesammelt wurden. Fängt es an, wie die Speichernutzung dieses Prototyps auszusehen? Es sieht so aus, und das ist definitiv nicht gut. Um die Profilerstellung zu vereinfachen, fügen wir das im ersten Schritt erwähnte Dienstprogramm hinzu. Hier ist der Code, der in Main.as hinzugefügt werden soll:
private Funktion init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); // Einstiegspunkt stage.addEventListener (MouseEvent.CLICK, createParticles); addEventListener (Event.ENTER_FRAME, updateParticles); addChild (neue Stats ()); _oldTime = getTimer ();
Vergiss nicht zu importieren net.hires.debug.Stats
und es ist bereit verwendet zu werden!
Die in Schritt 4 entwickelte Anwendung war ziemlich einfach. Es zeigte nur einen einfachen Partikeleffekt, verursachte jedoch eine Menge Ärger im Speicher. In diesem Schritt beginnen wir mit der Arbeit an einem Objektpool, um dieses Problem zu beheben.
Unser erster Schritt auf dem Weg zu einer guten Lösung besteht darin, darüber nachzudenken, wie die Objekte problemlos gebündelt werden können. In einem Objektpool müssen wir immer sicherstellen, dass das erstellte Objekt einsatzbereit ist und dass das zurückgegebene Objekt vollständig vom Rest der Anwendung "isoliert" ist (d. H. Keine Verweise auf andere Dinge enthält). Damit jedes gepoolte Objekt dazu gezwungen werden kann, erstellen wir ein Schnittstelle. Diese Schnittstelle definiert zwei wichtige Funktionen, die das Objekt haben muss: erneuern()
und zerstören()
. Auf diese Weise können wir diese Methoden immer aufrufen, ohne sich darüber Gedanken machen zu müssen, ob das Objekt über sie verfügt (oder nicht). Dies bedeutet auch, dass jedes Objekt, das wir bündeln möchten, diese Schnittstelle implementieren muss. Hier ist es also:
package public interface IPoolable function get zerstört (): Boolean; Funktion erneuern (): nichtig; Funktion destroy (): nichtig;
Da unsere Partikel Pool-fähig sind, müssen wir sie implementieren lassen IPoolfähig
. Grundsätzlich verschieben wir den gesamten Code von ihren Konstruktoren in die erneuern()
Funktion, und entfernen Sie alle externen Verweise auf das Objekt in der zerstören()
Funktion. So sollte es aussehen:
/ * INTERFACE IPoolable * / public-Funktion wird zerstört (): Boolean return _destroyed; public function renew (): void if (! _destroyed) return; _destroyed = falsch; graphics.beginFill (uint (Math.random () * 0xFFFFFF), 0.5 + (Math.random () * 0.5)); graphics.drawRect (-1,5, -1,5, 3, 3); graphics.endFill (); _angle = Math.random () * Math.PI * 2; Geschwindigkeit = 150; // Pixel pro Sekunde _lifeTime = 1000; // Milisekunden public function destroy (): void if (_destroyed) return; _destroyed = wahr; graphics.clear ();
Der Konstruktor sollte auch keine Argumente mehr benötigen. Wenn Sie Informationen an das Objekt übergeben möchten, müssen Sie dies jetzt über Funktionen tun. Aufgrund der Art und Weise, dass die erneuern()
Funktion funktioniert jetzt, müssen wir auch einstellen _zerstört
zu wahr
im Konstruktor, damit die Funktion ausgeführt werden kann.
Damit haben wir gerade unsere angepasst Partikel
Klasse, die sich als IPoolfähig
. Auf diese Weise kann der Objektpool einen Pool von Partikeln erstellen.
Es ist jetzt an der Zeit, einen flexiblen Objektpool zu erstellen, der jedes gewünschte Objekt zusammenfassen kann. Dieser Pool wird ein bisschen wie eine Fabrik wirken: anstatt die Neu
Wenn Sie ein Schlüsselwort zum Erstellen von Objekten erstellen, die Sie verwenden können, rufen wir stattdessen eine Methode im Pool auf, die ein Objekt an uns zurückgibt.
Der Einfachheit halber ist der Objektpool ein Singleton. Auf diese Weise können wir überall in unserem Code darauf zugreifen. Beginnen Sie, indem Sie eine neue Klasse mit dem Namen "ObjectPool" erstellen und den Code hinzufügen, um ihn zu einem Singleton zu machen:
package public class ObjectPool private statisch var _instance: ObjectPool; private static var _allowInstantiation: Boolean; öffentliche statische Funktion get instance (): ObjectPool if (! _instance) _allowInstantiation = true; _instance = new ObjectPool (); _allowInstantiation = false; return _instance; public function ObjectPool () if (! _allowInstantiation) throw new Fehler ("Versuch, ein Singleton zu instanziieren!");
Die Variable _allowInstantiation
ist der Kern dieser Singleton-Implementierung: Sie ist privat und kann nur von der eigenen Klasse geändert werden. Der einzige Ort, an dem sie geändert werden soll, ist das Erstellen der ersten Instanz.
Wir müssen jetzt entscheiden, wie die Pools in dieser Klasse gehalten werden. Da es global sein wird (d. H. Jedes Objekt in Ihrer Anwendung bündeln kann), müssen wir zunächst einen Weg finden, immer einen eindeutigen Namen für jeden Pool zu haben. Wie geht das? Es gibt viele Möglichkeiten, aber die beste, die ich bisher gefunden habe, ist die Verwendung der eigenen Klassennamen der Objekte als Poolnamen. Auf diese Weise könnten wir einen "Partikel" -Pool, einen "Feind" -Pool usw. haben ... aber es gibt noch ein anderes Problem. Klassennamen müssen nur innerhalb ihrer Pakete eindeutig sein, so dass beispielsweise eine Klasse "BaseObject" innerhalb des Pakets "Feinde" und eine Klasse "BaseObject" innerhalb des Pakets "Strukturen" zulässig sind. Das würde Probleme im Pool verursachen.
Die Idee, Klassennamen als Bezeichner für die Pools zu verwenden, ist nach wie vor großartig flash.utils.getQualifiedClassName ()
kommt um uns zu helfen Grundsätzlich generiert diese Funktion eine Zeichenfolge mit dem vollständigen Klassennamen, einschließlich aller Pakete. Jetzt können wir den qualifizierten Klassennamen jedes Objekts als Bezeichner für die jeweiligen Pools verwenden! Dies werden wir im nächsten Schritt hinzufügen.
Nun, da wir eine Möglichkeit haben, Pools zu identifizieren, ist es an der Zeit, den Code hinzuzufügen, mit dem sie erstellt werden. Unser Objektpool sollte flexibel genug sein, um sowohl statische als auch dynamische Pools zu unterstützen (wir haben in Schritt 3 darüber gesprochen, wissen Sie noch?). Wir müssen auch die Größe jedes Pools und die Anzahl der aktiven Objekte in jedem Pool speichern können. Eine gute Lösung dafür ist, eine private Klasse mit all diesen Informationen zu erstellen und alle Pools in einer Objekt
:
package public class ObjectPool private statisch var _instance: ObjectPool; private static var _allowInstantiation: Boolean; private var _pools: Objekt; öffentliche statische Funktion get instance (): ObjectPool if (! _instance) _allowInstantiation = true; _instance = new ObjectPool (); _allowInstantiation = false; return _instance; public function ObjectPool () if (! _allowInstantiation) throw new Fehler ("Versuch, ein Singleton zu instanziieren!"); _pools = ; class PoolInfo öffentliche var-Elemente: Vektor.; public var itemClass: Klasse; öffentliche var size: uint; public var active: uint; public var isDynamic: Boolean; public function PoolInfo (itemClass: Klasse, Größe: uint, isDynamic: Boolean = true) this.itemClass = itemClass; items = neuer Vektor. (Größe,! isDynamic); this.size = size; this.isDynamic = isDynamic; aktiv = 0; initialisieren(); private Funktion initialize (): void for (var i: int = 0; i < size; i++) items[i] = new itemClass();
Der obige Code erstellt die private Klasse, die alle Informationen zu einem Pool enthält. Wir haben auch die erstellt _pools
Objekt für alle Objektpools. Im Folgenden erstellen wir die Funktion, die einen Pool in der Klasse registriert:
public function registerPool (objectClass: Class, Größe: uint = 1, isDynamic: Boolean = true): void if (! (descriptionType (objectClass) .factory.implementsInterface. (@ type == "IPoolable"). length ()> 0)) werfen neuen Fehler ("Kann etwas nicht zusammenfassen, das IPoolable nicht implementiert!"); Rückkehr; varqualifizierterName: String = getQualifiedClassName (objectClass); if (! _pools [QualifiedName]) _pools [QualifiedName] = neue PoolInfo (objectClass, size, isDynamic);
Dieser Code sieht etwas kniffliger aus, aber keine Panik. Hier wird alles erklärt. Der Erste ob
Aussage sieht wirklich komisch aus. Sie haben diese Funktionen möglicherweise noch nie zuvor gesehen.
Fabrik
Etikett.implementsInterface
Etikett.IPoolfähig
Schnittstelle ist unter ihnen. Wenn ja, wissen wir, dass wir diese Klasse zum Pool hinzufügen können, da wir sie erfolgreich als Ich erhebe Einspruch
.Der Code nach dieser Prüfung erstellt nur einen Eintrag _pools
wenn einer nicht schon existierte Danach die PoolInfo
Konstruktor nennt das initialisieren()
Funktion innerhalb dieser Klasse, so dass der Pool effektiv mit der gewünschten Größe erstellt wird. Jetzt kann es benutzt werden!
Im letzten Schritt konnten wir die Funktion erstellen, die einen Objektpool registriert. Jetzt müssen wir ein Objekt abrufen, um es verwenden zu können. Es ist sehr einfach: Wir erhalten ein Objekt, wenn der Pool nicht leer ist, und geben es zurück. Wenn der Pool leer ist, prüfen wir, ob er dynamisch ist. Wenn dies der Fall ist, erhöhen wir die Größe, erstellen ein neues Objekt und geben es zurück. Wenn nicht, geben wir null zurück. (Sie können auch einen Fehler ausgeben, es ist jedoch besser, einfach null zurückzugeben, damit Ihr Code in dieser Situation funktioniert, wenn er auftritt.)
Hier ist die getObj ()
Funktion:
öffentliche Funktion getObj (objectClass: Class): IPoolable varqualifizierterName: String = getQualifiedClassName (objectClass); if (! _pools [QualifizierterName]) Neuen Fehler melden ("Objekt kann nicht aus einem Pool abgerufen werden, der nicht registriert wurde!"); Rückkehr; var returnObj: IPoolable; if (PoolInfo (_pools [QualifizierteName]). active == PoolInfo (_pools [QualifizierteName]). size) if (PoolInfo (_pools [QualifizierteName]). isDynamic) returnObj = new objectClass (); PoolInfo (_pools [QualifiedName]). Size ++; PoolInfo (_pools [QualifiedName]). Items.push (returnObj); else return null; else returnObj = PoolInfo (_pools [qualifizierterName]). items [PoolInfo (_pools [qualifizierterName]). active]; returnObj.renew (); PoolInfo (_pools [QualifizierteName]). Active ++; return returnObj;
In der Funktion überprüfen wir zunächst, ob der Pool tatsächlich existiert. Wenn diese Bedingung erfüllt ist, prüfen wir, ob der Pool leer ist: Wenn er aber dynamisch ist, erstellen wir ein neues Objekt und fügen es dem Pool hinzu. Wenn der Pool nicht dynamisch ist, stoppen wir den Code dort und geben einfach null zurück. Wenn der Pool noch ein Objekt enthält, erhalten wir das Objekt, das dem Poolanfang am nächsten liegt, und rufen auf erneuern()
darauf Das ist wichtig: der Grund, den wir nennen erneuern()
Bei einem Objekt, das sich bereits im Pool befand, soll sichergestellt werden, dass dieses Objekt in einem Zustand "verwertbar" angegeben wird.
Sie fragen sich wahrscheinlich: Warum benutzen Sie nicht auch diesen coolen Check mit descriptionType ()
in dieser Funktion? Nun, die Antwort ist einfach: descriptionType ()
erstellt ein XML jeden Wenn wir es einmal nennen, ist es sehr wichtig, die Erstellung von Objekten zu vermeiden, die viel Speicher benötigen und die wir nicht kontrollieren können. Außerdem reicht es nur zu prüfen, ob der Pool wirklich existiert: Wenn die übergebene Klasse nicht implementiert wird IPoolfähig
, Das bedeutet, dass wir nicht einmal einen Pool dafür schaffen könnten. Wenn es keinen Pool dafür gibt, fangen wir diesen Fall definitiv in unserem ob
Anweisung am Anfang der Funktion.
Wir können jetzt unsere ändern Main
Klasse und benutze den Objektpool! Hör zu:
private Funktion init (e: Event = null): void removeEventListener (Event.ADDED_TO_STAGE, init); // Einstiegspunkt stage.addEventListener (MouseEvent.CLICK, createParticles); addEventListener (Event.ENTER_FRAME, updateParticles); _oldTime = getTimer (); ObjectPool.instance.registerPool (Particle 200, true); private Funktion createParticles (e: MouseEvent): void var tempParticle: Particle; für (var i: int = 0; i < 10; i++) tempParticle = ObjectPool.instance.getObj(Particle) as Particle; tempParticle.x = e.stageX; tempParticle.y = e.stageY; addChild(tempParticle);
Klicken Sie auf Kompilieren und Profilieren Sie die Speichernutzung! Hier ist was ich habe:
Das ist irgendwie cool, oder??
Wir haben erfolgreich einen Objektpool implementiert, der uns Objekte gibt. Das ist erstaunlich! Aber es ist noch nicht vorbei. Wir erhalten immer noch nur Objekte, geben sie aber niemals zurück, wenn wir sie nicht mehr brauchen. Zeit zum Hinzufügen einer Funktion zum Zurückgeben von Objekten ObjectPool.as
:
öffentliche Funktion returnObj (obj: IPoolable): void varqualifizierterName: String = getQualifiedClassName (obj); if (! _pools [QualifizierterName]) Neuen Fehler melden ("Objekt kann nicht aus einem Pool zurückgegeben werden, der nicht registriert wurde!"); Rückkehr; var objIndex: int = PoolInfo (_pools [QualifiedName]). items.indexOf (obj); if (objIndex> = 0) if (! PoolInfo (_pools [QualifizierteName]). isDynamic) PoolInfo (_pools [QualifizierteName]). items.fixed = false; PoolInfo (_pools [QualifiedName]). Items.splice (objIndex, 1); obj.destroy (); PoolInfo (_pools [QualifizierteName]). Items.push (obj); if (! PoolInfo (_pools [QualifizierteName]). isDynamic) PoolInfo (_pools [QualifizierteName]). items.fixed = true; PoolInfo (_pools [QualifizierterName]). Active--;
Gehen wir die Funktion durch: Zuerst prüfen Sie, ob ein Pool des übergebenen Objekts vorhanden ist. Sie sind an diesen Code gewöhnt - der einzige Unterschied besteht darin, dass wir jetzt ein Objekt anstelle einer Klasse verwenden, um den qualifizierten Namen zu erhalten, dies ändert jedoch nicht die Ausgabe.).
Als Nächstes erhalten wir den Index des Elements im Pool. Wenn es nicht im Pool ist, ignorieren wir es einfach. Wenn wir uns vergewissert haben, dass sich das Objekt im Pool befindet, müssen wir den Pool brechen, an dem sich das Objekt gerade befindet, und das Objekt am Ende des Objekts erneut einfügen. Und warum? Da wir die verwendeten Objekte vom Anfang des Pools aus zählen, müssen wir den Pool so reorganisieren, dass alle zurückgegebenen und nicht verwendeten Objekte am Ende des Pools stehen. Und das tun wir in dieser Funktion.
Für statische Objektpools erstellen wir eine Vektor
Objekt mit fester Länge. Aus diesem Grund können wir nicht spleißen()
es und drücken()
Gegenstände zurück. Die Problemumgehung besteht darin, das zu ändern Fest
Eigentum von denen Vektor
s zu falsch
, Entfernen Sie das Objekt und fügen Sie es am Ende wieder hinzu. Ändern Sie dann die Eigenschaft wieder in wahr
. Wir müssen auch die Anzahl der aktiven Objekte verringern. Danach sind wir mit der Rückgabe des Objekts fertig.
Nachdem wir nun den Code erstellt haben, um ein Objekt zurückzugeben, können wir unsere Partikel dazu bringen, sich selbst in den Pool zurückzuholen, wenn sie das Ende ihrer Lebensdauer erreicht haben. Innerhalb Particle.as
:
Aktualisierung der öffentlichen Funktion (timePassed: uint): void // Bewegung des Teilchens x + = Math.cos (_angle) * _speed * timePassed / 1000; y + = Math.sin (_angle) * _speed * timePassed / 1000; // Kleine Erleichterung, damit Bewegung schön aussieht _speed - = 120 * timePassed / 1000; // Pflege der Lebensdauer und Entfernung _lifeTime - = timePassed; if (_lifeTime <= 0) parent.removeChild(this); ObjectPool.instance.returnObj(this);
Beachten Sie, dass wir einen Anruf hinzugefügt haben ObjectPool.instance.returnObj ()
da drin. Dadurch kehrt das Objekt in den Pool zurück. Wir können jetzt unsere App testen und profilieren:
Und los geht's! Stabiler Speicher, auch wenn Hunderte von Klicks gemacht wurden!
Sie wissen jetzt, wie Sie einen Objektpool erstellen und verwenden, um die Speichernutzung Ihrer App stabil zu halten. Die von uns erstellte Klasse kann überall verwendet werden, und es ist sehr einfach, Ihren Code daran anzupassen. Erstellen Sie zu Beginn Ihrer App Objektpools für jede Art von Objekt, das Sie bündeln möchten, und wann immer es eine gibt Neu
Schlüsselwort (bedeutet das Erstellen einer Instanz), ersetzen Sie es durch einen Aufruf der Funktion, die ein Objekt für Sie abruft. Vergessen Sie nicht, die Methoden der Schnittstelle zu implementieren IPoolfähig
erfordert!
Es ist sehr wichtig, die Speichernutzung stabil zu halten. Es erspart Ihnen später in Ihrem Projekt viel Ärger, wenn alles auseinander fällt, indem unrezyklierte Instanzen immer noch auf Ereignis-Listener reagieren, Objekte den verfügbaren Speicher füllen und der Garbage Collector läuft und alles verlangsamt. Eine gute Empfehlung ist, von nun an immer das Objekt-Pooling zu verwenden, und Sie werden feststellen, dass Ihr Leben wesentlich einfacher wird.
Beachten Sie auch, dass dieses Tutorial zwar auf Flash abzielte, die hier entwickelten Konzepte jedoch global sind: Sie können es auf AIR-Apps, mobilen Apps und an beliebigen Stellen verwenden. Danke fürs Lesen!