2016-01-20 18 views
5

Im testowania Swift 2.0 i nowego słowa kluczowego defer na placu zabaw:odroczyć funkcja nie działa w Swift 2.0

func branch() -> String { 

    var str = "" 

    defer { str += "xxx" } 
    str += "1" 

    let counter = 3; 

    if counter > 0 { 
     str += "2" 
     defer { str += "yyy" } 
     str += "3" 
    }  
    str += "4" 

    return str  
} 

let bran = branch() 

spodziewałem bran być "123yyy4xxx", ale faktycznie jest "123yyy4"

Dlaczego czy mój odroczenie (str += "xxx") nie działa zgodnie z oczekiwaniami?

+1

to działa, ale 'defer' jest wywoływana _after_ zakres wybiega zatem pierwszy Wywoływany jest 'return str', a następnie zakres kończy się i wywołuje twoje' odroczenie' i dodaje '' xxx' 'do instancji lokalnej, która się dzieje po powrocie wartości. – holex

Odpowiedz

7

Polecenie odroczenia odracza wykonanie, dopóki nie zostanie zakończony bieżący zakres.

To, co mówi jabłko. Tak więc instrukcja odroczenia zostanie wykonana po instrukcji return. Dlatego nie widać oczekiwanego wyniku.

5

Po pierwsze: wykonanie defer, ponieważ wyraźnie widać, dodając do niego .

teraz wyjaśnić, dlaczego zwrócona wartość nie odzwierciedla wartości Zmieniono:
Powodem tego jest to, że String jest niezmienna - kiedy piszesz str += something utworzyć zupełnie nową String instancji i przechowywać go wewnątrz str.

Jeśli napiszesz return str, która zwróci bieżące wystąpienie str, które jest 123yyy4. Następnie wywoływana jest nazwa defer i przypisuje ona zupełnie nową i niepowiązaną ze sobą definicję: String123yyy4xxx do . Ale to nie zmienia poprzedniego obiektu String przechowywanego wewnątrz str, po prostu go zastępuje i dlatego nie ma wpływu na return, który już "zdarzył się".

przypadku zmiany sposobu korzystania z NSMutableString zamiast będziesz zawsze działają na samym instancji, a wynik będzie zatem poprawnie wyjście 123yyy4xxx:

func branch() -> NSMutableString { 
    var str = NSMutableString() 
    defer { str.appendString("xxx") } 
    str.appendString("1") 
    let counter = 3; 
    if counter > 0 { 
     str.appendString("2") 
     defer { str.appendString("yyy") } 
     str.appendString("3") 
    } 
    str.appendString("4") 
    return str 
} 


let bran1 = branch() 

W tym kodzie Zwraca instancję przechowywane w str i odracza zmianę tej instancji , nie przypisuje nowej instancji, ale zmienia już istniejącą.

Przez wzgląd na wyjaśnienie można spróbować zobaczyć adres pamięci str na różnych etapach:

  • w czasie return
  • przed zmianą str w bloku defer
  • po zmianie go

Dla NSMutableString wszystkie trzy przypadki dadzą taki sam adres pamięci w g, że instancja pozostaje taka sama. Ten jednak drukuje dwa różne adresy pamięci, w wyniku czego zwracany łańcuch wskazuje na someAddress, a ten z opóźnieniem wskazuje na someOtherAddress.

+3

Zauważ, że 'unsafeAddressOf()' jest całkowicie pozbawione sensu w przypadku łańcucha Swift (lub dowolnego typu innego niż klasa), zwraca adres tymczasowo zmostkowanego NSString: http://stackoverflow.com/questions/32638879/swift-trings- i-adresy pamięci. –

+0

@MartinR hmm:/any Idea, czego powinienem użyć zamiast wizualizować zmianę pamięci instancji String? – luk2302

7

Greg jest poprawna, a jeśli chcesz, aby uzyskać ten sam wynik z kodem następnie można zrobić to w ten sposób:

var str = "" 

func branch() { 

    str = "" 
    defer { str += "xxx" } 
    str += "1" 


    let counter = 3 

    if counter > 0 { 
     str += "2" 
     defer { str += "yyy" } 
     str += "3" 
    } 
    str += "4" 

} 

branch() 
str //"123yyy4xxx" 
+2

Rozwiązania oparte na zmiennych globalnych prawie nigdy nie są dobrymi rozwiązaniami. Zapomniałeś także zresetować zmienną 'str', więc wielokrotne wywoływanie' branch() 'daje różne wyniki. – Cristik

+0

Kod zaktualizowany i dziękuję za sugestię. –