2014-06-20 10 views
8

Nel discorso avanzata Swift dal WWDC 2014, l'altoparlante ha dato questo esempio di una funzione memoizer utilizzando farmaci generici:corso della vita della memoria trattenuta Swift chiusure

func memoize<T: Hashable, U>(body: (T)->U) -> (T)->U { 
    var memo = Dictionary<T, U>() 
    return { x in 
     if let q = memo[x] { return q } 
     let r = body(x) 
     memo[x] = r 
     return r 
    } 
} 

Ho problemi confezionamento mia testa intorno alla vita di che memo var. Ogni invocazione della funzione memoized fibonacci contiene un forte riferimento ad essa? E se sì, come lasceresti quella memoria quando hai finito?

risposta

12

in C/Objective-C Blocks terminologia, memo è una __block variabili (a Swift, non è necessario per scrivere esplicitamente __block per acquisire le variabili per riferimento). La variabile può essere assegnata a un blocco (chiusura) e tutti gli ambiti che vedono quella variabile vedranno cambiamenti da qualsiasi altra variabile (condividono un riferimento alla variabile). La variabile sarà valida fino a quando un blocco (chiusura) che lo utilizza (c'è solo un blocco che lo utilizza in questo caso) è ancora vivo. Dopo che l'ultimo blocco che lo utilizza viene deallocato, la variabile non rientra nell'ambito. Come funziona è un dettaglio di implementazione.

Se questa variabile aveva il tipo di puntatore oggetto, l'oggetto puntato verrà mantenuto da qualsiasi blocco che lo cattura. Tuttavia, in questo caso, la variabile è Dictionary, un tipo di struttura, che è un tipo di valore. Quindi non c'è nessuna gestione della memoria di cui preoccuparsi. La variabile è la struct e la struct vive fino a quando la variabile. (La struttura stessa può allocare memoria altrove e liberarla nel suo distruttore, ma è completamente gestita internamente dalla struttura e l'esterno non dovrebbe sapere o preoccuparsene.)

Generalmente non è necessario preoccuparsi di come __block le variabili funzionano internamente. Ma fondamentalmente, la variabile è avvolta in un tipo di oggetto "oggetto" semplificato, con la "variabile" effettiva che è un campo di questo "oggetto", che è gestito dalla memoria attraverso il conteggio dei riferimenti. I blocchi che li catturano contengono "forti riferimenti" a questa cosa pseudo-oggetto - quando un blocco viene creato sullo heap (tecnicamente, quando vengono copiati dai blocchi di stack sull'heap) che utilizza questa variabile __block, aumenta il conteggio dei riferimenti ; quando un blocco che lo utilizza viene deallocato, diminuisce il conteggio dei riferimenti. Quando il conteggio dei riferimenti diventa 0, questo pseudo- "oggetto" viene deallocato, invocando prima il distruttore appropriato per il suo tipo di variabile.

Per rispondere alla tua domanda, la "funzione di fibonacci memorizzata" è un blocco (chiusura). Ed è quello che contiene un forte riferimento a qualsiasi cosa detenga la variabile memo. Le "invocazioni" non hanno riferimenti forti o deboli; quando viene invocata una funzione, utilizza il riferimento che ha la funzione stessa.La durata della variabile memo è la durata della "funzione fibonacci memorizzata" in questo caso, perché è l'unica chiusura che cattura questa variabile.

2

Il blocco interno (valore di ritorno) manterrà il memo, il che significa che il memo persisterà fino a quando il blocco restituito verrà mantenuto in giro.

Una nuova istanza di memo verrà creata ogni volta che viene chiamata la funzione memoize. La chiamata al blocco restituito non creerà nuove istanze di memo.

La memoria verrà rilasciata quando il blocco restituito esce dallo scope.

3

Ogni chiamata di memoize() creerà la propria variabile memo, indipendente dalle altre chiamate. Vive fintanto che la chiusura fa riferimento alla vita; quando viene rilasciata la chiusura, vengono rilasciate anche le variabili acquisite da essa (in questo caso, memo).

Si può pensare a questo come di restituire una struttura come in questo pseudocodice:

struct Closure<T,U> 
{ 
    var memo: Dictionary<T, U> 
    func call(t: T): U 
    { 
     .... 
    } 
} 

func memoize<T: Hashable, U>(body: (T)->U) -> Closure<T,U> 
{ 
    return Closure(memo: Dictionary<T, U>()) 
} 
+0

Non è lo stesso codice, perché più chiusure possono catturare questa variabile, e la variabile sarebbe condivisa dallo stato tra tutte le chiusure. Inoltre, lo stato variabile viene anche condiviso tra chiusura e codice al di fuori della chiusura (ad esempio, il codice nella funzione 'memoize' direttamente può anche leggere e scrivere' memo'). – newacct

+1

@newacct Il codice serve a illustrare questo caso particolare, non le chiusure in generale. – hamstergene

Problemi correlati