@ noescape atrybut w Swift 1.2

Jest nowy atrybut w Swift 1.2 z parametrami zamknięcia w funkcjach i jak mówi dokumentacja:

Oznacza to, że parametr jest zawsze wywoływany (lub przekazywany jako @ parametr noescape w wywołaniu), co oznacza, że nie może przeżyj całe życie połączenia.

W moim rozumieniu, wcześniej moglibyśmy użyć [weak self], aby zamknięcie nie miało silnego odniesienia do np. jego klasy, a self może być zerowe lub instancją, gdy closure jest wykonywane, ale teraz @noescape oznacza, że zamknięcie nigdy nie zostanie wykonane, jeśli klasa zostanie deinitalizowana. Czy dobrze to Rozumiem?

A jeśli się nie mylę, to po co miałbym używać @noescape zamknięcia w funkcji regularnej, skoro zachowują się bardzo podobnie?

Author: Dániel Nagy, 2015-02-10

3 answers

@noescape Można używać w ten sposób:

func doIt(code: @noescape () -> ()) {
    /* what we CAN */

    // just call it
    code()
    // pass it to another function as another `@noescape` parameter
    doItMore(code)
    // capture it in another `@noescape` closure
    doItMore {
        code()
    }

    /* what we CANNOT do *****

    // pass it as a non-`@noescape` parameter
    dispatch_async(dispatch_get_main_queue(), code)
    // store it
    let _code:() -> () = code
    // capture it in another non-`@noescape` closure
    let __code = { code() }

    */
}

func doItMore(code: @noescape () -> ()) {}

Dodanie @noescape gwarantuje, że zamknięcie nie będzie gdzieś przechowywane, używane w późniejszym czasie lub używane asynchronicznie.

Z punktu widzenia wywołującego nie ma potrzeby dbania o żywotność przechwyconych zmiennych, ponieważ są one używane w wywołanej funkcji lub wcale. Jako bonus, możemy użyć domyślnego self, ratując nas przed wpisaniem self..

func doIt(code: @noescape () -> ()) {
    code()
}

class Bar {
    var i = 0
    func some() {
        doIt {
            println(i)
            //      ^ we don't need `self.` anymore!
        }
    }
}

let bar = Bar()
bar.some() // -> outputs 0

Również z punktu widzenia kompilatora (co udokumentowano w uwagi do wydania):

Umożliwia to niewielkie optymalizacje wydajności.

 143
Author: rintaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-18 02:20:45

Jednym ze sposobów myślenia jest to, że każda zmienna wewnątrz bloku @noescape nie musi być silna (nie tylko jaźń).

Są również możliwe optymalizacje, ponieważ gdy zmienna zostanie przydzielona, a następnie zawinięta w blok, nie może być normalnie dealokowana na końcu funkcji. Więc musi być przydzielony na stercie i użyć łuku do dekonstrukcji. W Objective-C, musisz użyć słowa kluczowego "_ _ block", aby upewnić się, że zmienna zostanie utworzona w przyjaznym bloku sposób. Swift automatycznie wykryje, więc słowo kluczowe nie jest potrzebne, ale koszt jest taki sam.

Jeśli zmienne są przekazywane do bloku @ nosecape, mogą być zmiennymi stosu i nie potrzebują ARC do dealokacji.

Zmienne teraz nawet nie muszą być odwołujące się do zera słabe zmienne (które są droższe niż niebezpieczne wskaźniki), ponieważ będą one gwarantowane, że będą "żywe" przez cały okres życia bloku.

Wszystko to powoduje szybsze i bardziej optymalne kod. I zmniejsza koszty na korzystanie z bloków @ autoklosure (które są bardzo przydatne).

 28
Author: Michael Gray,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-10 20:33:06

(w nawiązaniu do odpowiedzi Michaela Graya powyżej.)

Nie jestem pewien, czy jest to specjalnie udokumentowane dla Swifta, czy nawet kompilator Swifta w pełni z tego korzysta. Ale standardowym projektem kompilatora jest przydzielanie pamięci dla instancji na stosie, jeśli kompilator wie, że wywoływana funkcja nie będzie próbowała zapisać wskaźnika do tej instancji w stercie, a jeśli funkcja spróbuje to zrobić, wyda błąd w czasie kompilacji.

Jest to szczególnie korzystne przy przechodzeniu nie-skalarne typy wartości (takie jak enums, structs, closures), ponieważ ich kopiowanie jest potencjalnie znacznie droższe niż zwykłe przekazanie wskaźnika do stosu. Przydzielanie instancji jest również znacznie tańsze (jedna instrukcja w porównaniu z wywołaniem malloc ()). Więc jest to podwójne zwycięstwo, jeśli kompilator może dokonać tej optymalizacji.

Ponownie, to, czy dana wersja kompilatora Swift faktycznie ma, musiałby być stwierdzony przez zespół Swift, lub musiałbyś przeczytać kod źródłowy, gdy otwierają go. Z powyższego cytatu na temat " minor optimization "wynika, że albo nie, albo zespół Swift uważa to za"minor". Uznałbym to za znaczącą optymalizację.

Przypuszczalnie atrybut jest tam tak, że (przynajmniej w przyszłości) kompilator będzie w stanie wykonać tę optymalizację.

 8
Author: David Goodine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-24 20:37:24