Czy IBOutlety powinny być mocne czy słabe pod ARC?


551

Tworzę wyłącznie na iOS 5 przy użyciu ARC. Czy IBOutlets UIView(s) i (podklasy) powinny być stronglub weak?

Następujące:

@property (nonatomic, weak) IBOutlet UIButton *button;

Pozbyłby się tego wszystkiego:

- (void)viewDidUnload
{
    // ...
    self.button = nil;
    // ...
}

Czy są jakieś problemy? Szablony są używane, strongpodobnie jak automatycznie generowane właściwości tworzone podczas łączenia bezpośrednio z nagłówkiem z edytora „Interface Builder”, ale dlaczego? Ten UIViewControllerjuż ma strongodniesienie do jego, viewktóry zachowuje swoje widoki.


11
Uwaga: IBOutletCollection()nie może być weak, w przeciwnym razie zwraca jako nil.
ohho,

Xcode 8.2.1 używa słaby podczas tworzenia IBOutletów za pomocą konstruktora interfejsów. Jednak wiele odpowiedzi tutaj na SO zaleca użycie silnych.
neoneye

1
@neoneye Właśnie próbowałem z xcode 8.3.2 przeciąganie z serii ujęć do szybkiego pliku i domyślniestrong
CupawnTae

Odpowiedzi:


252

Obecnie zalecaną najlepszą praktyką firmy Apple jest, aby IBOutlety były silne, chyba że słaba jest szczególnie potrzebna, aby uniknąć cyklu przechowywania. Jak wspomniano powyżej Johannes, zostało to skomentowane podczas sesji „Implementing UI Designs in Interface Builder” z WWDC 2015, gdzie inżynier Apple powiedział:

Ostatnią opcją, na którą chcę zwrócić uwagę, jest typ pamięci, który może być mocny lub słaby. Ogólnie rzecz biorąc, powinieneś wzmocnić swój punkt sprzedaży, szczególnie jeśli podłączasz go do widoku podrzędnego lub do ograniczenia, które nie zawsze będzie zachowane przez hierarchię widoków. Jedynym czasem, kiedy naprawdę potrzebujesz osłabić gniazdko, jest posiadanie niestandardowego widoku, który odwołuje się do czegoś, co tworzy kopię zapasową hierarchii widoków i ogólnie nie jest to zalecane.

Zapytałem o to na Twitterze inżyniera z zespołu IB, który potwierdził, że domyślne powinno być silne, a dokumenty programistów są aktualizowane.

https://twitter.com/_danielhall/status/620716996326350848 https://twitter.com/_danielhall/status/620717252216623104


33
Czy to naprawdę prawda, czy też odpowiedź na ponad 300 głosów jest poprawna? Zauważyłem, że InterfaceBuilder domyślnie używa słabego
trybu,

4
Ten z ponad 400 głosami jest poprawny, ale nieaktualny. Ponieważ iOS 6 viewDidUnload nie jest wywoływany, więc nie ma korzyści z posiadania słabych gniazd.
kjam

7
@kjam są korzyści. Przede wszystkim nie powinieneś mieć silnego odniesienia do czegoś, czego nie stworzyłeś. Po drugie, wzrost wydajności jest znikomy. Nie naruszaj najlepszych praktyk w programowaniu tylko dlatego, że jakiś facet, nawet dobrze ustawiony, powiedział, że jest to 10 mikrosekund szybciej. Wyczyść kod, nie próbuj grać w kompilator optymalizujący. Koduj wydajność tylko wtedy, gdy została zmierzona w konkretnym przypadku jako problem.
Cameron Lowell Palmer

5
Pozwól mi się z tobą nie zgodzić. „Trzymanie silnego odniesienia do czegoś, czego nie stworzyłeś” zdarza się cały czas w Objective-C. Dlatego istnieje liczenie referencji , a nie jednego właściciela. Czy masz jakieś odniesienia do kopii zapasowej tego zalecenia? Czy mógłbyś wymienić inne zalety słabych punktów sprzedaży?
kjam

4
Oto wideo WWDC wspomniane w odpowiedzi developer.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn

450

OSTRZEŻENIE, ODPOWIEDZIALNA ODPOWIEDŹ : ta odpowiedź jest nieaktualna w rozumieniu WWDC 2015, aby uzyskać poprawną odpowiedź, patrz zaakceptowana odpowiedź (Daniel Hall) powyżej. Ta odpowiedź zostanie zapisana.


Podsumowane z biblioteki programistów :

Z praktycznego punktu widzenia gniazda w iOS i OS X powinny być zdefiniowane jako deklarowane właściwości. Outlety powinny być na ogół słabe, z wyjątkiem tych od właściciela pliku do obiektów najwyższego poziomu w pliku stalówki (lub, w iOS, scenie scenorysu), które powinny być mocne. Utworzone punkty sprzedaży zazwyczaj są zatem domyślnie słabe, ponieważ:

  • Utworzone ujścia, na przykład w widokach podrzędnych widoku kontrolera widoku lub okna kontrolera okna, są dowolnymi odniesieniami między obiektami, które nie implikują własności.

  • Silne gniazda są często określane przez klasy frameworka (na przykład ujście widoku UIViewController lub ujście okna NSWindowController).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    

10
W jaki sposób uzyskano link „biblioteka programistów”, aby przejść do określonej części strony Apple Doc? Ilekroć łączę z dokumentami Apple, zawsze prowadzi do górnej części strony (nawet jeśli interesująca treść znajduje się w połowie strony). Dzięki.
bearMountain

68
Skopiowałem link z panelu nawigacji po lewej stronie. : D
Alexsander Akers,

27
Co oznacza „z wyjątkiem tych od właściciela pliku do obiektów najwyższego poziomu w pliku stalówki (lub, w iOS, scenie scenorysu)”?
Van Du Tran,

16
@ VanDuTran - oznacza obiekty w NIB, które znajdują się na poziomie głównym, tzn. Powiedziano, że utworzyłeś tam inny widok, który nie jest bezpośrednio widokiem podrzędnym widoku głównego, wtedy musi mieć silne odniesienie.
mattjgalloway

6
Najwyższy poziom oznacza, że ​​gdy spojrzysz na stalówkę, obiekt pojawi się na liście po lewej stronie. Prawie wszystkie stalówki mają UIView - może to być jedyny obiekt najwyższego poziomu. Jeśli dodasz inne elementy, które pojawiają się na liście, są to „obiekty najwyższego poziomu”
David H

50

Podczas gdy dokumentacja zaleca używanie weakwłaściwości do widoków podrzędnych, od iOS 6 wydaje się być dobrym rozwiązaniem strong(domyślny kwalifikator własności). Jest to spowodowane zmianą w UIViewControllertym, że widoki nie są już rozładowywane.

  • Przed iOS 6, jeśli utrzymywałeś silne linki do widoków podrzędnych widoku kontrolera, jeśli główny widok kontrolera widoków został rozładowany, będą one podtrzymywać widoki podrzędne, dopóki kontroler widoku będzie w pobliżu.
  • Od wersji iOS 6 widoki nie są już rozładowywane, ale ładowane raz, a następnie są wyświetlane tak długo, jak długo jest tam kontroler. Tak silne właściwości nie będą miały znaczenia. Nie będą również tworzyć silnych cykli odniesienia, ponieważ wskazują na silny wykres odniesienia.

To powiedziawszy, jestem rozdarty między używaniem

@property (nonatomic, weak) IBOutlet UIButton *button;

i

@property (nonatomic) IBOutlet UIButton *button;

w iOS 6 i później:

  • Użycie weakwyraźnie oznacza, że ​​kontroler nie chce mieć prawa do przycisku.

  • Ale pominięcie weaknie boli w iOS 6 bez rozładowywania widoku i jest krótsze. Niektórzy mogą zauważyć, że jest to również szybsze, ale jeszcze nie spotkałem aplikacji, która jest zbyt wolna z powodu weak IBOutlets.

  • Nieużywanie weakmoże być postrzegane jako błąd.

Konkluzja: Od iOS 6 nie możemy się już mylić, dopóki nie używamy odciążania widoku. Czas na imprezę. ;)


To prawda, ale nadal możesz chcieć samodzielnie zwolnić widok. W takim przypadku musisz nilręcznie ustawić wszystkie swoje punkty sprzedaży .
hypercrypt,

PS: weakjest nieco tańszy w ARM64: D
hypercrypt

Zgadza się, jeśli zaimplementujesz odciążanie widoku, właściwym rozwiązaniem są weakwłaściwości lub __weakzmienne instancji. Chciałem tylko zauważyć, że istnieje tutaj mniejszy potencjał błędu. Co do weakbycia tańszym na arm64, nawet nie widziałem prawdziwego problemu z wydajnością weak IBOutletzs na armv7. :)
Tammo Freese,

W takim przypadku strongma to również sens. strongjest szkodliwy tylko wtedy, gdy używasz rozładowywania widoku - ale kto to robi obecnie? :)
Tammo Freese

2
@Rocotilos Pierwszy iPhone miał bardzo ograniczoną pamięć RAM. Jeśli dobrze pamiętam, 128 MB, pozostawiając około 10 MB dla aktywnej aplikacji. Mając mały ślad pamięci był kluczowy, dlatego nastąpiło rozładowanie widoku. Zmieniło się to, ponieważ mamy teraz coraz więcej pamięci RAM, a Apple UIView zoptymalizował UIViews w iOS 6, dzięki czemu przy ostrzeżeniach pamięci można zwolnić dużo pamięci bez zwalniania widoku.
Tammo Freese,

34

Nie widzę z tym żadnego problemu. Przed ARC zawsze robiłem moje IBOutlety assign, ponieważ są już zachowane przez ich superwizje. Jeśli je weakwykonasz, nie powinieneś ich zerować w viewDidUnload, jak zauważyłeś.

Jedno zastrzeżenie: możesz obsługiwać iOS 4.xw projekcie ARC, ale jeśli tak, nie możesz go używać weak, więc musisz je zrobić assign, w którym to przypadku nadal chcesz zerować odniesienie, viewDidUnloadaby uniknąć zwisający wskaźnik. Oto przykład wiszącego błędu wskaźnika, którego doświadczyłem:

UIViewController ma UITextField dla kodu pocztowego. Używa CLLocationManager do odwrócenia geokodowania lokalizacji użytkownika i ustawienia kodu pocztowego. Oto wywołanie zwrotne delegata:

-(void)locationManager:(CLLocationManager *)manager
   didUpdateToLocation:(CLLocation *)newLocation
          fromLocation:(CLLocation *)oldLocation {
    Class geocoderClass = NSClassFromString(@"CLGeocoder");
    if (geocoderClass && IsEmpty(self.zip.text)) {
        id geocoder = [[geocoderClass alloc] init];
        [geocoder reverseGeocodeLocation:newLocation completionHandler:^(NSArray *placemarks, NSError *error) {
            if (self.zip && IsEmpty(self.zip.text)) {
                self.zip.text = [[placemarks objectAtIndex:0] postalCode];
            }
        }];    
    }
    [self.locationManager stopUpdatingLocation];
}

Odkryłem, że jeśli odrzuciłem ten widok we właściwym czasie i nie wprowadziłem zerowego self.zip viewDidUnload, wywołanie zwrotne dla delegata może zgłosić wyjątek złego dostępu do self.zip.text.


4
Rozumiem również, że weakwłaściwości nie muszą być uzupełniane viewDidUnload. Ale dlaczego szablon Apple do tworzenia placówek zawiera [self setMySubview:nil]?
Yang Meyer

3
Czy istnieją przypadki, w których użycie silnego / zatrzymanego dla IBOutlet może powodować problem? A może jest to po prostu nadmiarowa retencja, co oznacza zły styl kodowania, ale nie wpłynie na kod?
Enzo Tran

1
Czy istnieje coś takiego jak zbędne utrzymanie? Jeśli istnieje dodatkowa retencja, spowoduje to, że nie zostanie poprawnie policzona, a zatem nie zostanie zwolniona tak szybko, jak to możliwe, ponieważ istnieje dodatkowa retencja w liczbie retencji.
karlbecker_com

25

IBOutletpowinien być silny, ze względu na wydajność. Zobacz temat Storyboard Reference, Strong IBOutlet, Scene Dock w iOS 9

Jak wyjaśniono w tym akapicie, ujścia widoków podrzędnych widoku kontrolera widoku mogą być słabe, ponieważ te widoki podrzędne są już własnością obiektu najwyższego poziomu pliku stalówki. Jednak gdy ujście jest zdefiniowane jako słaby wskaźnik, a wskaźnik jest ustawiony, ARC wywołuje funkcję środowiska wykonawczego:

id objc_storeWeak(id *object, id value);

Spowoduje to dodanie wskaźnika (obiektu) do tabeli przy użyciu wartości obiektu jako klucza. Ta tabela jest nazywana słabą tabelą. ARC używa tej tabeli do przechowywania wszystkich słabych wskaźników twojej aplikacji. Teraz, gdy wartość obiektu zostanie zwolniona, ARC będzie iterować po słabej tabeli i ustawi słabe odniesienie na zero. Alternatywnie ARC może zadzwonić:

void objc_destroyWeak(id * object)

Następnie obiekt jest niezarejestrowany i obiekt objc_destroyWeak wywołuje ponownie:

objc_storeWeak(id *object, nil)

Księgowość związana ze słabym odniesieniem może potrwać 2–3 razy dłużej niż wydanie silnego odniesienia. Tak więc słabe odniesienie wprowadza narzut środowiska wykonawczego, którego można uniknąć, po prostu definiując gniazda jako silne.

Jak sugeruje Xcode 7 strong

Jeśli oglądasz sesję 407 WWDC 2015 Implementowanie projektów interfejsu użytkownika w Konstruktorze interfejsów , sugeruje (transkrypcja z http://asciiwwdc.com/2015/sessions/407 )

Ostatnią opcją, na którą chcę zwrócić uwagę, jest typ pamięci, który może być mocny lub słaby.

Zasadniczo powinieneś wzmocnić swoje ujście, szczególnie jeśli podłączasz ujście do widoku podrzędnego lub do ograniczenia, które nie zawsze będzie zachowane przez hierarchię widoków.

Jedynym czasem, kiedy naprawdę potrzebujesz osłabić gniazdko, jest posiadanie niestandardowego widoku, który odwołuje się do czegoś, co tworzy kopię zapasową hierarchii widoków i ogólnie nie jest to zalecane.

Więc wybiorę silny i kliknę Connect, który wygeneruje mój wylot.


1
Świetna odpowiedź, która wyjaśnia rzeczywisty powód
dlaczego-

To dobrze i wszystko, ale widziałem przecieki pochodzące z rozpoznawania gestów zaimplementowanych w scenorysie.
thibaut noah

1
Nie rozumiem tej linii. „Jedynym czasem, kiedy naprawdę potrzebujesz osłabić gniazdko, jest posiadanie niestandardowego widoku, który odwołuje się do czegoś, co tworzy kopię zapasową hierarchii widoków i ogólnie nie jest to zalecane”. Jakieś przykłady?
user1872384,

Obliczyłem czas deinit, który zajmuje słaby i silny, i jest dokładnie taki sam.
touti

Ale w szybkim przypadku jest to bardziej prawdopodobne. Słabe referencje są szybsze.
thesummersign

20

W rozwoju iOS ładowanie NIB nieco różni się od rozwoju Maca.

W rozwoju Maca IBOutlet jest zwykle słabym odniesieniem: jeśli masz podklasę NSViewController, zachowany zostanie tylko widok najwyższego poziomu, a po zwolnieniu kontrolera wszystkie jego widoki podrzędne i ujścia zostaną automatycznie zwolnione.

UiViewController wykorzystuje kodowanie wartości klucza do ustawienia gniazd przy użyciu silnych referencji. Więc kiedy zwolnisz swój UIViewController, widok z góry zostanie automatycznie zwolniony, ale musisz także cofnąć przydział wszystkich jego gniazd w metodzie zwolnienia.

W tym poście z Big Nerd Ranch omawiają ten temat, a także wyjaśniają, dlaczego użycie silnej referencji w IBOutlet nie jest dobrym wyborem (nawet jeśli w tym przypadku jest zalecane przez Apple).


16
Wyjaśnia to na rok 2009. W przypadku ARC sytuacja uległa znacznej zmianie.
Dafydd Williams,

1
:( link Big Nerd Ranch jest martwy… ale naprawdę muszę go przeczytać. Ktoś wie więcej szczegółów na temat tego postu, więc mogę go znaleźć?
Motti Shneor

@MottiShneor nie martw się, to nie jest wielka sprawa, ponieważ link był o czasach przed ARC i nie jest już istotny.
Sergey Grischyov,

18

Jedną rzecz, na którą chciałbym tutaj zwrócić uwagę, a mianowicie, pomimo tego, co inżynierowie Apple stwierdzili w swoim własnym filmie WWDC 2015 tutaj:

https://developer.apple.com/videos/play/wwdc2015/407/

Apple ciągle zmienia zdanie na ten temat, co mówi nam, że nie ma jednej właściwej odpowiedzi na to pytanie. Aby pokazać, że nawet inżynierowie Apple są podzieleni na ten temat, spójrz na najnowszy przykładowy kod Apple, a zobaczysz, że niektórzy ludzie używają słabych, a niektórzy nie.

W tym przykładzie Apple Pay zastosowano słaby: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_switch

Podobnie jak w przypadku obrazu w obrazie: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFlayer

Podobnie jak przykład Listera: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Podobnie jak przykład z lokalizacją podstawową: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swlement

Podobnie jak widok kontrolera przykład podglądu: https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html#//apple_ref/doc/uid/TP40016546-Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift-DontLinkElementID_5

Podobnie jak w przypadku przykładu HomeKit: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP40015048-HMCatalog_Hift_Serv_Data_Widoku

Wszystkie są w pełni zaktualizowane dla iOS 9 i wszystkie używają słabych gniazd. Z tego dowiadujemy się, że A. Problem nie jest tak prosty, jak się wydaje. B. Apple wielokrotnie zmieniało zdanie, i C. Możesz używać wszystkiego, co cię uszczęśliwia :)

Specjalne podziękowania dla Paula Hudsona (autora www.hackingwithsift.com), który udzielił mi wyjaśnień i odniesień do tej odpowiedzi.

Mam nadzieję, że to trochę lepiej wyjaśnia ten temat!

Dbać.


Od jakiegoś czasu sprawdzam ten problem i nie znalazłem żadnych konkretnych odpowiedzi. Ponieważ powyższy link sugeruje, że oba są w porządku i ogólnie są zgodne z tym, co Xcode automatycznie sugeruje.
subin272



5

Wygląda na to, że coś się zmieniło przez lata, a teraz Apple zaleca stosowanie silnego w ogóle. Dowody na ich sesję WWDC znajdują się w sesji 407 - Implementacja projektów interfejsu użytkownika w interfejsie Konstruktora i rozpoczyna się o 32:30. Moja uwaga z tego, co mówi, brzmi (prawie, jeśli nie dokładnie, cytując go):

  • połączenia wyjściowe powinny być silne, szczególnie jeśli połączymy widok podrzędny lub ograniczenie, które nie zawsze jest zachowywane przez hierarchię widoków

  • słabe połączenie wylotowe może być potrzebne podczas tworzenia niestandardowych widoków, które zawierają odniesienia do czegoś, co zostało utworzone w hierarchii widoków i ogólnie nie jest zalecane

Na innych oddziałach powinno być teraz zawsze silne, o ile niektóre z naszego niestandardowego widoku nie tworzą cyklu przechowywania z niektórymi widokami w hierarchii widoków

EDYTOWAĆ :

Niektórzy mogą zadać pytanie. Czy utrzymywanie go z silnym odwołaniem nie tworzy cyklu przechowywania, ponieważ kontroler widoku głównego i widok właściciela zachowuje odniesienie do niego? Albo dlaczego to się stało? Myślę, że odpowiedź jest wcześniejsza w tym wykładzie, kiedy opisują, jak stalówki są tworzone z Xib. Istnieje oddzielna końcówka stworzona dla VC i dla widoku. Myślę, że to może być powód, dla którego zmieniają zalecenia. Byłoby jednak miło uzyskać głębsze wyjaśnienie od Apple.


4

Myślę, że najważniejszą informacją jest: Elementy w Xib są automatycznie wyświetlane w pod-widoku. Wywiady to NSArray. NSArray jest właścicielem jego elementów. itd. mają silne wskazówki. Więc w większości przypadków nie chcesz tworzyć kolejnego silnego wskaźnika (IBOutlet)

A dzięki ARC nie musisz nic robić viewDidUnload

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.