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
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 @IBOutlet
s 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
}
}
}
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.
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ół
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.
Scenariusze
Możesz mieć dwa możliwe scenariusze:
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.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 :
Więcej o unowned {[2] } inner workings znajdziesz tutaj .Unowned jest szybszy i pozwala na niezmienność i nonoptionality.
Jeśli nie potrzebujesz słabego, nie używaj go.
*
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).
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:
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 przezx!
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 synonimemunowned(safe)
obecnie, ale intencją jest to, że zostanie zoptymalizowany dounowned(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 orazunowned
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, gdyunowned
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
Podekscytowany, co?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.
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ęcself
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. }
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.
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)
}
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