ProgrammazioneSviluppatore iOS

Как работает механизм value и reference capture в closures Swift? Какие потенциальные опасности могут возникнуть при захвате, и как их избежать?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

In Swift, le chiusure (closures) possono "catturare" valori e riferimenti dal contesto circostante. Se il valore catturato è un tipo con valore (struct, enum), la chiusura copia i dati. Se viene catturato un riferimento (ad esempio, una classe), la chiusura memorizza il riferimento all'istanza della classe, il che può portare a un ciclo di retention se non si utilizza [weak self] o [unowned self].

Il meccanismo di cattura consente di implementare attività asincrone e callback, tuttavia un uso scorretto ostacola la gestione della memoria.

Esempio:

class MyClass { var value = 10 func doSomething() { let closure = { [weak self] in print(self?.value ?? "nil") } closure() } }

In questo esempio, viene utilizzato [weak self] per evitare un ciclo di riferimenti forti, poiché è in corso un'operazione asincrona.

Domanda ingannevole.

Domanda: "L'uso di [weak self] garantisce sempre l'assenza di perdite di memoria quando self viene catturato nella chiusura?"

Risposta: No, se all'interno della chiusura appare un riferimento forte (ad esempio, tramite variabili intermedie), anche con [weak self] è possibile ottenere un ciclo di retention. Ad esempio:

let closure = { [weak self] in let strongSelf = self strongSelf?.doSomething() }

Se si collega la chiusura a un oggetto a lungo termine (ad esempio, NotificationCenter), si possono ottenere cicli di retention se ci sono altri riferimenti a self.

Esempi di errori reali dovuti a una scarsa comprensione delle sfumature dell'argomento.


Storia 1

In un progetto di applicazione iOS, uno sviluppatore ha dimenticato di specificare [weak self] quando ha passato la chiusura a una richiesta di rete. Di conseguenza, mentre la richiesta era in corso, l'oggetto view controller non veniva liberato, anche se l'utente aveva chiuso lo schermo. Ciò ha portato a perdite di memoria durante un uso intensivo delle operazioni di rete.


Storia 2

In un complesso sistema di abbonamento a un timer, un oggetto che si era iscritto tramite una chiusura senza utilizzare weak continuava a eseguire le proprie azioni anche dopo che lo schermo era stato rimosso dalla memoria. Questo ha portato a ripetuti invii di elementi UI che non erano più nella gerarchia, causando crash dell'applicazione.


Storia 3

Nell'implementazione della caching dei dati, la chiusura catturava un riferimento a self senza l'annotazione [unowned self] in un oggetto cache a lungo termine. Dopo la distruzione dell'oggetto originale, un tentativo di accesso a self dalla chiusura ha causato un accesso a memoria già liberata e ha fatto crashare l'applicazione.