ProgrammierungiOS/Mobile Entwickler

Wie funktionieren Closures in Swift, was unterscheidet sie von Funktionen und welche Aspekte des Speichermanagements sind mit ihrer Verwendung verbunden?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Geschichte der Frage:

Closures als Konzept stammen aus funktionalen Sprachen und ermöglichen es, Codeblöcke als Werte zu übergeben. In Swift wurden Closures (analog zu Lambdas aus anderen Sprachen) von Anfang an eingeführt. Dadurch können asynchrone Aufrufe elegant realisiert, Aktionen delegiert, Sammlungen sortiert und gefiltert werden, usw.

Problem:

Closures erfassen Variablen aus dem umgebenden Kontext. Wenn unter diesen Variablen Verweise auf Klassenobjekte sind, kann ein Retain Cycle entstehen, insbesondere wenn das Closure als Eigenschaft dieser Klasse gespeichert wird. Es ist auch wichtig, den Unterschied zwischen escaping und non-escaping Closures zu verstehen und zu wissen, wie die Erfassung von Werten funktioniert.

Lösung:

Swift implementiert Closures als eigenständige Objekte, die den Kontext erfassen können. Der Besitzer des Closures muss bei der Architektur vorsichtig sein.

Beispielcode:

class Performer { var onComplete: (() -> Void)? func doWork() { onComplete = { print("Arbeit abgeschlossen!") } } } // Erfassung von self innerhalb des Closures class Test { var value = 0 func start() { DispatchQueue.global().async { [weak self] in self?.value = 1 } } }

Schlüsselmerkmale:

  • Closures können Werte durch Referenz oder durch Wert erfassen
  • escaping/non-escaping beeinflusst den Lebenszyklus des Closures
  • Closures als gespeicherte Eigenschaften können leicht Retain Cycles erzeugen

Fangfragen.

Wenn das Closure self nicht explizit erfasst, kann ein Retain Cycle entstehen?

Ja, wenn das Closure als starke Eigenschaft der Klasse gespeichert wird und auf self verweist, entsteht ein Retain Cycle, selbst wenn Sie nicht explizit self schreiben. Verwenden Sie [weak self]/[unowned self] oder machen Sie das Closure lokal, wenn möglich.

Was ist der Unterschied zwischen escaping und non-escaping Closures?

Ein escaping Closure kann nach Abschluss der Funktion aufgerufen werden und wird normalerweise für asynchrone Operationen verwendet. Ein non-escaping wird vor dem Abschluss der Funktion ausgeführt. Bei escaping ist die Reihenfolge der Erfassung von self anders.

Beispielcode:

func asyncWork(completion: @escaping () -> Void) { DispatchQueue.global().async { completion() } }

Kann ein Closure die Werte der erfassten Variablen ändern?

Ja, wenn ein Closure eine Variable erfasst, die als var deklariert ist, kann es ihren Wert ändern (für Werttypen). Für Klassen ist es eine Referenz, und die Eigenschaften können immer geändert werden.

Beispielcode:

var value = 1 let closure = { value += 1 } closure() print(value) // 2

Typische Fehler und Anti-Pattern

  • Das Setzen von Closures als Eigenschaften der Klasse und der Zugriff auf self ohne [weak self].
  • Die Deklaration von Closures in zu großen Größen – erschwert das Verständnis und die Fehlersuche.
  • Verwendung von escaping, wo man sich mit non-escaping begnügen könnte.

Beispiel aus dem Leben

Negativer Fall

ViewController speichert ein Closure als Eigenschaft (z. B. completionHandler) und greift darin direkt auf self zu. Infolgedessen entsteht ein Retain Cycle: ViewController => Closure => ViewController. Das Ausschalten des Bildschirms gibt den Speicher nicht frei.

Vorteile: Der Code ist kurz und prägnant.

Nachteile: Speicherlecks, ViewController bleibt im Speicher "hängen", potenzielle Bugs.

Positiver Fall

Es wird [weak self] oder [unowned self] innerhalb des Closures verwendet, oder das Closure wird nicht länger als der Lebenszyklus des Objekts gespeichert. Überprüfung solcher Stellen bei Code-Reviews.

Vorteile: Korrekte Freigabe von Ressourcen, keine unvorhersehbaren Speicherlecks.

Nachteile: [weak self] erfordert Vorsicht beim Dereferenzieren, mögliche stumme Abstürze bei falscher Verwendung.