Czy zawsze będziemy używać [nieumarłej jaźni] wewnątrz zamknięcia w szybkim

W WWDC 2014 sesja 403 Intermediate Swift i transkrypcja , był następujący slajd

Tutaj wpisz opis obrazka

Mówca powiedział, że jeśli nie użyjemy [unowned self] Tam, to będzie wyciek pamięci. Czy to znaczy, że powinniśmy zawsze używać [unowned self] wewnątrz zamknięcia?

Na linii 64 kontrolera ViewController.swift z aplikacji Swift Weather , nie używam [unowned self]. Ale zaktualizowałem interfejs użytkownika używając @IBOutlets jak self.temperature i self.loadingIndicator. Może być OK bo wszystkie @IBOutlet s zdefiniowałem weak. Ale dla bezpieczeństwa, czy zawsze powinniśmy używać [unowned self]?

class TempNotifier {
  var onChange: (Int) -> Void = {_ in }
  var currentTemp = 72
  init() {
    onChange = { [unowned self] temp in
      self.currentTemp = temp
    }
  }
}
Author: Ryan Heitner, 2014-06-20

7 answers

Nie, są czasy, w których nie chciałbyś używać [unowned self]. Czasami chcesz, aby zamknięcie uchwyciło siebie, aby upewnić się, że nadal jest w pobliżu, zanim zostanie wywołane zamknięcie.

Przykład: wykonanie asynchronicznego żądania sieci

Jeśli wykonujesz asynchroniczne żądanie sieciowe, czy chcesz, aby zamknięcie zachowało self po zakończeniu żądania. Ten obiekt może być w inny sposób dealokowany, ale nadal chcesz być w stanie zajmij się wykończeniem żądania.

Kiedy stosować unowned self lub weak self

Jedynym momentem, w którym naprawdę chcesz użyć [unowned self] lub [weak self], jest utworzenie silnego cyklu odniesienia. Silny cykl odniesienia ma miejsce wtedy, gdy istnieje pętla własności, w której obiekty stają się właścicielami siebie nawzajem (być może przez stronę trzecią) i dlatego nigdy nie zostaną rozdzielone, ponieważ oba zapewniają, że wzajemnie się trzymają.

W konkretnym przypadku zamknięcia, po prostu trzeba zdać sobie sprawę, że każda zmienna, która jest odwołany wewnątrz niego, dostaje" własność " przez zamknięcie. Tak długo, jak zamknięcie jest wokół, te obiekty są gwarantowane, aby być wokół. Jedynym sposobem, aby zatrzymać tę własność, jest wykonanie [unowned self] lub [weak self]. Więc jeśli klasa posiada zamknięcie i to zamknięcie przechwytuje silne odniesienie do tej klasy, to mamy silny cykl odniesienia między zamknięciem a klasą. Obejmuje to również, jeśli klasa posiada coś, co jest własnością zamknięcia.

W przykładzie z filmu

W przykładzie na slajdzie, TempNotifier posiada zamknięcie poprzez zmienną member onChange. Gdyby nie zadeklarowali self jako unowned, Zamknięcie również posiadałoby self tworząc silny cykl odniesienia.

Różnica między unowned a weak

Różnica między unowned i weak jest taka, że weak jest zadeklarowana jako opcjonalna, podczas gdy unowned nie jest. Deklarując to weak masz do obsługi sprawy, że może być Zero wewnątrz zamknięcia w pewnym momencie. Jeśli spróbujesz uzyskać dostęp do zmiennej unowned, która ma wartość nil, spowoduje to awarię całego programu. Więc używaj unowned tylko wtedy, gdy masz pewność, że zmienna będzie zawsze wokół

 709
Author: drewag,
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
2014-06-23 15:34:29

Aktualizacja 11/2016

Napisałem na ten temat artykuł rozszerzający tę odpowiedź (patrząc na SIL, aby zrozumieć, co robi ARC), sprawdź to tutaj .

Oryginalna odpowiedź

Poprzednie odpowiedzi tak naprawdę nie dają prostych zasad, kiedy używać jednego nad drugim i dlaczego, więc pozwolę sobie dodać kilka rzeczy.

Dyskusja niewzruszona lub słaba sprowadza się do pytania życia zmiennej i zamknięcia, które odwołuje to.

swift weak vs unowned

Scenariusze

Możesz mieć dwa możliwe scenariusze:

  1. Zamknięcie ma ten sam czas życia zmiennej, więc zamknięcie będzie osiągalne Tylko do momentu, gdy zmienna będzie osiągalna . Zmienna i zamknięcie mają ten sam czas życia. W takim przypadku należy zadeklarować odniesienie jako unowned. Częstym przykładem jest [unowned self] używany w wielu przykładach małych zamknięć, które robią coś w kontekście ich rodzic i to, że nie są wymienione nigdzie indziej, nie przeżyją swoich rodziców.

  2. Czas Życia zamknięcia jest niezależny od czasu trwania zmiennej, zamknięcie może być odwołane, gdy zmienna nie jest już dostępna. W tym przypadku należy zadeklarować odniesienie jako słabe i sprawdzić, czy nie jest zerowe przed użyciem (nie Wymuś rozpakowania). Powszechnym tego przykładem jest [weak delegate] można zobaczyć w niektórych przykładach zamknięcia odwołujących się do zupełnie niepowiązanych (lifetime-wise) delegate object.

Rzeczywiste Użycie

Więc,które będą / powinny faktycznie używać większość razy?

Cytując Joe groffa z Twittera :

Unowned jest szybszy i pozwala na niezmienność i nonoptionality.

Jeśli nie potrzebujesz słabego, nie używaj go.

Więcej o unowned {[2] } inner workings znajdziesz tutaj .

* zwykle określane również jako nieumarłe (bezpieczne) do wskazuje, że przed uzyskaniem dostępu do nieautoryzowanego odniesienia wykonywane są kontrole środowiska wykonawczego (które prowadzą do awarii dla nieprawidłowych referencji).

 139
Author: Umberto Raimondi,
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
2017-06-19 06:37:59

Jeśli self może być zerowe w użyciu zamknięcia [słabe self] .

If self nigdy nie będzie zerowe w użyciu zamknięcia [unowned self] .

Dokumentacja Apple Swift ma świetną sekcję z obrazami wyjaśniającymi różnicę między używaniem strong, słaba , oraz unowned w zamknięcia:

Https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

 58
Author: TenaciousJay,
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
2017-06-29 15:47:56

Oto genialne cytaty z forów programistów Apple opisane pyszne szczegóły:

unowned vs unowned(safe) vs unowned(unsafe)

unowned(safe) jest nie posiadającym odnośnika, który zapewnia o dostępie, że obiekt wciąż żyje. To coś w rodzaju słabego opcjonalnego odniesienia to jest domyślnie rozpakowane przez x! za każdym razem, gdy jest dostępny. unowned(unsafe) jest jak __unsafe_unretained W ARC-to nie posiadanie reference, ale nie ma kontroli runtime, czy obiekt jest jeszcze żywy na dostęp, więc zwisające odniesienia sięgną do pamięci śmieci. unowned jest zawsze synonimem unowned(safe) obecnie, ale intencją jest to, że zostanie zoptymalizowany do unowned(unsafe) w -Ofast kompiluje się, gdy kontrole runtime są wyłączone.

unowned vs weak

unowned w rzeczywistości używa znacznie prostszej implementacji niż weak. Natywne Obiekty Swift posiadają dwa zliczniki referencji oraz unowned linki do unowned reference count zamiast strong liczba referencji . Obiekt jest deinicjalizowany, gdy jego strong reference count osiąga zero, ale w rzeczywistości nie jest dealokowany, dopóki unowned reference count również trafia zero. Powoduje to, że pamięć jest trzymane nieco dłużej, gdy nie ma żadnych odniesień, ale że nie jest zwykle problemem, gdy unowned jest używany, ponieważ powiązane obiekty i tak powinny mieć prawie równe życia, a to znacznie prostsze i niższym obciążeniem niż implementacja oparta na stoliku bocznym używana do zerowanie słabych odniesień.

Update: In modern Swift weak wewnętrznie używa tego samego mechanizmu co unowned robi. Porównanie to jest więc błędne, ponieważ porównuje Objective-C {[12] } z Swift unonwed.

Powody

jaki jest cel utrzymania pamięci przy życiu po uzyskaniu referencji 0? Co się stanie, jeśli kod spróbuje coś zrobić z obiekt korzystający z nieautoryzowanego odniesienia po jego deinicjalizacji?

The pamięć jest utrzymywana przy życiu, dzięki czemu jej liczba zatrzymań jest nadal dostępna. W ten sposób, gdy ktoś próbuje zachować silne odniesienie do obiekt unowned, runtime może sprawdzić, czy silna liczba referencji jest większa od zera, aby zapewnić bezpieczne zachowanie obiekt.

co się dzieje z posiadaniem lub nieujawnionymi referencjami posiadanymi przez obiekt? Jest ich żywotność oddzielona od obiektu, gdy jest deinicjalizowana lub czy ich pamięć jest również zachowana do momentu dealokacji obiektu po czy ostatni odnośnik unowned został wydany?

Wszystkie zasoby należące do obiektu są zwalniane, gdy tylko obiekt ostatnia mocna Referencja jest uwalniana, a jej deinit jest uruchamiany. Unowned odwołania tylko podtrzymują pamięć-oprócz nagłówka z liczy się odniesienie, jego zawartość to śmieci.

Podekscytowany, co?
 44
Author: Valentin Shergin,
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
2017-05-23 12:34:50

Pomyślałem, że dodam kilka konkretnych przykładów specjalnie dla kontrolera widoku. Wiele wyjaśnień, nie tylko tutaj na temat Stack Overflow, jest naprawdę dobre, ale lepiej pracuję z przykładami z prawdziwego świata (@drewag miał dobry początek w tym): {]}

  • Jeśli masz zamknięcie do obsługi odpowiedzi z żądań sieciowych użyj weak, ponieważ są one długotrwałe. Kontroler widoku może się zamknąć przed żądanie kończy się, więc self nie wskazuje już na ważny obiekt, gdy zamknięcie jest dzwoniłem.
  • Jeśli masz zamknięcie, które obsługuje zdarzenie na przycisku. Może to być unowned, ponieważ gdy tylko kontroler widoku zniknie, przycisk i wszystkie inne elementy, z których może się odnosić self, znikną w tym samym czasie. Blok zamknięcia również zniknie w tym samym czasie.

    class MyViewController: UIViewController {
          @IBOutlet weak var myButton: UIButton!
          let networkManager = NetworkManager()
          let buttonPressClosure: () -> Void // closure must be held in this class. 
    
          override func viewDidLoad() {
              // use unowned here
              buttonPressClosure = { [unowned self] in
                  self.changeDisplayViewMode() // won't happen after vc closes. 
              }
              // use weak here
              networkManager.fetch(query: query) { [weak self] (results, error) in
                  self?.updateUI() // could be called any time after vc closes
              }
          }
          @IBAction func buttonPress(self: Any) {
             buttonPressClosure()
          }
    
          // rest of class below.
     }
    
 40
Author: possen,
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
2017-11-10 23:53:06

Jest tu kilka świetnych odpowiedzi. Ale ostatnie zmiany w sposobie, w jaki Swift implementuje słabe referencje, powinny zmienić słabe decyzje każdego z nas dotyczące korzystania z siebie. Poprzednio, jeśli potrzebowałeś najlepszej wydajności używanie nieumarłej jaźni było lepsze od słabej jaźni, o ile możesz być pewien, że jaźń nigdy nie będzie zerowa, ponieważ dostęp do nieumarłej jaźni jest znacznie szybszy niż dostęp do słabej jaźni.

Ale Mike Ash udokumentował, jak Swift zaktualizował implementację słabych VAR-ów, aby używać tabele boczne i jak to znacznie poprawia słabe wyniki własne.

Https://mikeash.com/pyblog/friday-qa-2017-09-22-swift-4-weak-references.html

Teraz, gdy nie ma znaczącej kary wydajności dla słabego siebie, uważam, że powinniśmy domyślnie używać go w przyszłości. Zaletą słabego ja jest to, że jest to opcja, która znacznie ułatwia pisanie bardziej poprawnego kodu, to w zasadzie powód, dla którego Swift jest takim świetnym językiem. Możesz myśleć, że wiesz, który sytuacje są bezpieczne dla użycia unowned self, ale moje doświadczenie przeglądania wielu innych programistów kodu jest, większość nie. naprawiłem wiele awarii, w których unowned self został dealokowany, zwykle w sytuacjach, w których wątek tła kończy się po dealokacji kontrolera.

Błędy i awarie to najbardziej czasochłonne, bolesne i kosztowne elementy programowania. Staraj się pisać poprawny kod i ich unikać. Polecam uczynić z niej regułę, aby nigdy nie zmuszać do rozpakowywania i nigdy nie używaj nieumarłej jaźni zamiast słabej jaźni. Nic nie stracisz, gdy siła czasów rozpakowuje się i nie powściągliwa ja naprawdę jest Bezpieczna. Ale zyskasz wiele dzięki wyeliminowaniu trudnych do znalezienia i debugowania awarii i błędów.

 21
Author: Randy Hill,
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
2017-12-26 22:31:48

Według Apple-Doc

  • Słabe odwołania są zawsze typu opcjonalnego i automatycznie stają się zerowe, gdy instancja, do której się odwołują, jest dealokowana.

  • Jeśli przechwycone odniesienie nigdy nie stanie się zerowe, powinno być zawsze przechwycone jako niepodpisane odniesienie, a nie słabe odniesienie

Przykład-

    // if my response can nil use  [weak self]
      resource.request().onComplete { [weak self] response in
      guard let strongSelf = self else {
        return
      }
      let model = strongSelf.updateModel(response)
      strongSelf.updateUI(model)
     }

    // Only use [unowned self] unowned if guarantees that response never nil  
      resource.request().onComplete { [unowned self] response in
      let model = self.updateModel(response)
      self.updateUI(model)
     }
 2
Author: Jack,
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
2017-08-09 10:52:07