2014-06-20 30 views
8

W rozmowie zaawansowane Swift z WWDC 2014, głośnik dał tego przykład memoizer funkcji przy użyciu rodzajowych:Żywotność zachowanej w pamięci Swift closures

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 
    } 
} 

Mam problemy zawijania moja głowa wokół życia to memo var. Czy każde wywołanie funkcji memoised fibonacci ma silne odniesienie do niej? A jeśli tak, to jak zwolniłbyś tę pamięć, gdy skończysz?

Odpowiedz

12

w C/Objective-C Blocks terminologii memo jest __block zmienna (w Swift, nie trzeba jawnie napisz __block, aby przechwycić zmienne przez odniesienie). Zmienna może być przypisana do bloku (zamknięcia) i wszystkich zakresów, które widzą, że zmienna zobaczy zmiany z dowolnej innej zmiennej (współużytkują odniesienie do zmiennej). Zmienna będzie ważna, dopóki jakiś blok (zamknięcie), który jej używa (jest tylko jeden blok, który używa go w tym przypadku) jest wciąż żywy. Po zwolnieniu ostatniego bloku, zmienna wykracza poza zakres. Jak to działa, jest szczegółem implementacji.

Jeśli ta zmienna ma typ wskaźnika obiektu, to wskazywany obiekt zostanie zachowany przez bloki przechwytujące. Jednak w tym przypadku zmienną jest Dictionary, typ struktury, który jest typem wartości. Dlatego nie trzeba się martwić o zarządzanie pamięcią. Zmienna jest strukturą, a struktura żyje tak długo, jak zmienna. (Sama struktura może przydzielić pamięć gdzie indziej i zwolnić ją w swoim destruktorze, ale to jest wewnętrznie obsługiwane przez strukturę, a zewnętrzna strona nie powinna o tym wiedzieć ani się tym nie przejmować.)

Zazwyczaj nie trzeba się martwić o to, jak __block zmienne działają wewnętrznie. Zasadniczo zmienna jest zawijana w uproszczony "obiekt", rzeczywista "zmienna" jest polem tego "obiektu", którym zarządza się pamięcią poprzez liczenie odwołań. Bloki, które je przechwytują, posiadają "silne referencje" do tego pseudoobiektu - kiedy blok zostanie utworzony na stercie (technicznie, gdy są one kopiowane z bloków stosu do sterty), który używa tej zmiennej __block, zwiększa ona liczbę odnośników ; gdy blok korzystający z niego jest zwolniony, zmniejsza liczbę odwołań. Gdy licznik odwołań wynosi 0, ten pseudo "obiekt" zostaje zwolniony, wywołując najpierw odpowiedni destruktor dla jego typu zmiennej.

Aby odpowiedzieć na pytanie, "zapamiętana funkcja fibonacci" jest blokiem (zamknięciem). I to właśnie ma silne odniesienie do tego, co zawiera zmienną memo. "Inwokacje" nie mają silnych lub słabych odniesień do niego; gdy funkcja jest wywoływana, używa odwołania, które sama funkcja ma.Żywotność zmiennej memo jest czasem życia "zapamiętanej funkcji fibonacci" w tym przypadku, ponieważ jest to jedyne zamknięcie przechwytujące tę zmienną.

2

Blok wewnętrzny (wartość zwracana) zachowuje notatkę, co oznacza, że ​​notatka będzie się utrzymywać tak długo, jak długo będzie zwracana zwrócona blck.

Nowa instancja memo zostanie utworzona za każdym razem, gdy wywoływana jest funkcja zapamiętania. Wywołanie zwróconego bloku nie spowoduje utworzenia nowych instancji memo.

Pamięć zostanie zwolniona, gdy zwrócony blok wykracza poza zakres.

3

Każda inwokacja memoize() utworzy własną zmienną memo, niezależnie od innych wywołań. Żyje tak długo, jak odnosi się do tego zamknięcie; po zwolnieniu zamknięcia zmienione przez niego zmienne (w tym przypadku memo) również zostaną zwolnione.

Można myśleć o tym jak o powrocie struct jak w tym Pseudokod:

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

To nie to samo, co ten kod, ponieważ wiele zamknięć może przechwycić tę zmienną, a zmienna byłaby stanem wspólnym dla wszystkich zamknięć. Dodatkowo stan zmienny jest również dzielony między zamknięciem i kodem poza zamknięciem (to jest kod w funkcji 'memoize' bezpośrednio może również odczytywać i zapisywać' memo'). – newacct

+1

@newacct Kod ma na celu zilustrowanie tego konkretnego przypadku, a nie zamknięcia w ogóle. – hamstergene