Geschichte der Frage:
Escape-Analyse ist ein Begriff aus der Compiler-Optimierung und dem Speichermanagement. In Swift ist es wichtig wegen der aktiven Nutzung von Closures und ARC. Es bezieht sich auf das Konzept von escaping und non-escaping Closures, die bestimmen, ob Closures den Geltungsbereich der Funktion verlassen können.
Problem:
Die falsche Bestimmung des Typs des Closures führt zu Speicherbesitzfehlern, Lecks, unerwarteten Variablenübernahmen und Leistungseinbußen. Man muss genau verstehen, wann ein Closure „entkommt“ (escapes) und wann nicht, und sie korrekt kennzeichnen.
Lösung:
Swift erfordert, dass escaping Closures explizit mit dem Attribut @escaping gekennzeichnet werden. Non-escaping Closures können nur innerhalb der Funktion erfasst werden, sie haben automatisch ein effizienteres Speichermanagement und können erfasste Variablen sicherer verwenden.
Beispiel für den Unterschied:
// non-escaping closure (schneller, wird nach dem Aufruf nicht gespeichert) func performSync(block: () -> Void) { block() } // escaping closure (kann gespeichert und später ausgeführt werden) var storedCompletion: (() -> Void)? func performAsync(block: @escaping () -> Void) { storedCompletion = block }
Wichtige Merkmale:
Kann man ein als non-escaping definiertes Closure in escaping ändern, ohne den ursprünglichen Funktionscode zu ändern?
Nein. Das Attribut @escaping muss explizit in der Funktionssignatur angegeben werden. Wenn ein Closure innerhalb der Funktion außerhalb übergeben wird - wird nur ein escaping Closure benötigt, sonst gibt der Compiler einen Fehler aus.
Ist es sicher, self innerhalb eines escaping Closures ohne weak/unowned Capture zu übergeben?
Nein. Ein escaping Closure kann potenziell einen Retain Cycle erzeugen, wenn es self mit einer starken Referenz erfasst. Man muss den Capture explizit verwalten:
someAsync { [weak self] in self?.doSomething() }
Ist jedes escaping Closure global (wird im Speicher bis zum Ende des Programms gehalten)?
Nein. Sein Lebenszyklus hängt vom Speicherbereich ab. Wenn ein Closure nur vorübergehend gespeichert oder die Eigenschaft auf null gesetzt wird, wird es freigegeben, sobald die Eigenschaft ihren Besitzer verliert. Nur Closures, die Referenzen auf globale Objekte haben oder in globalen Variablen gespeichert sind, werden global.
Innerhalb einer Methodenklasse speichert man ein Closure ohne @escaping (Compilerfehler), später wird es behoben, man vergisst den Schutz vor Retain Cycles - die App hat Speicherlecks.
Vorteile:
Nachteile:
Der Entwickler prüft immer, wo ein escaping Closure erforderlich ist, erfasst self als weak/unowned, beseitigt Lecks, arbeitet sicher mit dem Speicher.
Vorteile:
Nachteile: