Чи повинен IBOutlets бути сильним чи слабким за ARC?


551

Я розробляю виключно для iOS 5 за допомогою ARC. Якщо IBOutlets до UIViews (і підкласи) має бути strongабо weak?

Наступні:

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

Позбулось би всього цього:

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

Чи є проблеми з цим? У шаблонах використовуються strongтакі ж, як автоматично створені властивості, створені під час підключення безпосередньо до заголовка з редактора "Інтерфейс", але чому? У нього UIViewControllerвже є strongпосилання на нього, viewяке зберігає свої підгляди.


11
Як примітка, IBOutletCollection()не повинно бути weak, інакше вона повертається як nil.
ой

Xcode 8.2.1 використовує слабкий під час створення IBOutlets через конструктор інтерфейсів. Однак багато відповідей тут на SO радить використовувати сильні.
neoneye

1
@neoneye Я щойно спробував перетягнути з розкадровки до швидкого файлу xcode 8.3.2 і він за замовчуваннямstrong
CupawnTae

Відповіді:


252

В даний час рекомендується найкраща практика від компанії Apple для IBOutlets бути сильним , якщо слабким спеціально не потрібно щоб уникнути збереження циклу. Як згадував Йоганнес вище, це було прокоментовано під час сесії "Впровадження дизайнів інтерфейсу в Builder інтерфейсів" WWDC 2015, де інженер Apple сказав:

І останній варіант, який я хочу зазначити - це тип зберігання, який може бути сильним або слабким. Загалом, ви повинні зробити вашу розетку сильною, особливо якщо ви підключаєте розетку до підвідного або до обмеження, яке не завжди буде зберігатися ієрархією перегляду. Єдиний раз, коли вам дійсно потрібно зробити розетку слабкою, це якщо у вас є власний перегляд, який посилається на щось резервне копіювання ієрархії перегляду, і взагалі це не рекомендується.

Я попросив про це у Twitter інженеру команди IB, і він підтвердив, що за замовчуванням має бути сильний і що документи розробників оновлюються.

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


33
Це справді правда, чи відповідь із 300+ оновленнями є правильним? Я помітив, що InterfaceBuilder за замовчуванням використовує слабке, коли ви перетягуєте Ctrl із
дошки розкадрівки

4
Той, що має більше 400 голосів, є правильним, але застарілим. Оскільки iOS 6 viewDidUnload не викликається, тому немає переваг для слабких розеток.
kjam

7
@kjam є переваги. Перш за все, ви не повинні чітко посилатися на те, що ви не створили. По-друге, приріст продуктивності незначний. Не порушуйте кращих практик програмування просто тому, що якийсь хлопець, навіть добре поставлений хлопець, сказав, що це на 10 мікросекунд швидше. Ясний намір коду, не намагайтеся грати в оптимізатор компілятора. Код для продуктивності лише тоді, коли він був виміряний у конкретному випадку, що є проблемою.
Камерон Лоуелл Палмер

5
Дозвольте мені не погодитися з вами. "Посилання на чітке посилання на те, що ви не створили", постійно відбувається в "Objective-C". Ось чому існує підрахунок посилань , а не один власник. Чи є у вас посилання на резервну копію цієї рекомендації? Чи можете ви перелічити інші переваги слабких торгових точок?
kjam

4
Ось відео WWDC, згадане у відповіді developer.apple.com/videos/play/wwdc2015/407/?time=1946
petrsyn

450

ПОПЕРЕДЖЕННЯ, ВІДПОВІДАЛЬНИЙ ВІДПОВІДЬ : Ця відповідь не є актуальною відповідно до WWDC 2015, для правильної відповіді зверніться до прийнятої відповіді (Daniel Hall) вище. Ця відповідь залишиться для запису.


Узагальнено з бібліотеки розробників :

З практичної точки зору, в iOS та OS X розетки слід визначати як оголошені властивості. Як правило, розетки мають бути слабкими, за винятком тих, хто від власника файлу, до об'єктів верхнього рівня у файлі nib (або, в iOS, сцені з розповідь), який повинен бути сильним. Таким чином, торгові точки, які ви створюєте, зазвичай за замовчуванням слабкі, оскільки:

  • Розетки, які ви створюєте, наприклад, для підпогляду подання контролера перегляду або вікна контролера вікон, - це довільні посилання між об'єктами, які не передбачають права власності.

  • Сильні розетки часто визначаються рамками класів (наприклад, розетка перегляду UIViewController або вікно розетки NSWindowController).

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

10
Як ви отримали посилання "бібліотека розробників", щоб перейти до конкретної частини сторінки яблучного документа? Щоразу, коли я посилаюся на документи з яблуками, він завжди посилається на верхню частину сторінки (навіть якщо вміст, що цікавить, знаходиться на половині сторінки). Дякую.
ведмідьМаунт

68
Я скопіював посилання з панелі навігації зліва. : D
Александер Акерс

27
Що означає "за винятком тих, від власника файлу до об'єктів найвищого рівня у файлі nib (або, в iOS, сцені розкадрівки)"?
Ван Дю Тран

16
@VanDuTran - це означає, що об'єкти в NIB знаходяться на рівні кореня, тобто, скажімо, ви створили інший погляд у ньому, який не є безпосередньо підглядом головного виду, тоді він повинен мати чітку орієнтацію.
mattjgalloway

6
Верхній рівень означає, що, дивлячись на ручку, об’єкт з’являється у списку зліва. Практично всі ниби мають UIView в них - це може бути єдиний об'єкт верхнього рівня. Якщо ви додасте інші елементи, і вони відображаються у списку, вони є "об'єктами вищого рівня"
David H

50

Хоча в документації рекомендується використовувати weakвластивості для підпрезентацій, оскільки iOS 6, здається, добре використовувати strongнатомість (класифікатор власності за замовчуванням). Це викликано зміною того, UIViewControllerщо погляди більше не завантажуються.

  • Перед iOS 6, якщо ви зберігали міцні посилання на підпогляди подання контролера навколо, якщо основний вигляд контролера перегляду буде завантажений, вони тримаються на підпоглядах до тих пір, поки контролер перегляду буде навколо.
  • Оскільки iOS 6, подання більше не завантажуються, а завантажуються один раз, а потім тримаються навколо, поки там є їх контролер. Так що сильні властивості не мають значення. Вони також не створюватимуть сильних еталонних циклів, оскільки вони вказують на сильний опорний графік.

Це сказало, що я розривається між використанням

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

і

@property (nonatomic) IBOutlet UIButton *button;

в iOS 6 і після:

  • При використанні weakчітко зазначено, що контролер не хоче володіти кнопкою.

  • Але опускання weakне зашкодить в iOS 6 без вивантаження перегляду, і коротше. Деякі можуть відзначити, що це також швидше, але я ще не стикаюся з додатком, який занадто повільний через weak IBOutlets.

  • Не використання weakможе сприйматися як помилка.

Підсумок: З iOS 6 ми більше не можемо помилитися з цим, доки не будемо використовувати вивантаження огляду. Час вечірки. ;)


Це правда, але ви, можливо, все ж захочете розвантажити подання самостійно. У такому випадку вам доведеться встановити всі торгові точки nilвручну.
гіперкрипт

PS: weakв ARM64 дещо дешевше: D
гіперкрипт

Правильно, якщо ви реалізуєте розвантаження представлення даних, шлях до змінних weakвластивостей чи __weakпримірників. Я просто хотів зазначити, що тут є менше можливостей для помилок. Щодо weakдешевшого в arm64, я навіть не бачив проблеми з реальністю роботи з weak IBOutlets на armv7. :)
Таммо Фрізе

І в цьому випадку strongє сенс. strongшкідливо лише в тому випадку, якщо ви використовуєте вивантаження огляду, але хто робить ці дні? :)
Таммо Фрізе

2
@Rocotilos У першого iPhone була дуже обмежена оперативна пам’ять. Якщо я пам'ятаю правильно, 128 Мб, залишаючи близько 10 Мб для активного додатка. Маючи невеликий слід пам’яті було вирішальним, тому відбулося розвантаження огляду. Це змінилося, оскільки тепер у нас все більше оперативної пам'яті, і Apple оптимізувала UIView в iOS 6, так що за попередженнями про пам’ять можна звільнити багато пам’яті, не розвантажуючи представлення даних.
Таммо Фрізе

34

Я не бачу жодної проблеми з цим. Перед ARC, я завжди робив свої IBOutlets assign, оскільки вони вже зберігають свої огляди. Якщо ви їх робите weak, вам не доведеться знімати їх у viewDidUnload, як ви вказуєте.

Одне застереження: Ви можете підтримати iOS 4.x у проекті ARC, але якщо це зробити, ви не можете його використовувати weak, тож вам доведеться їх зробити assign, і в такому випадку ви все одно хочете скасувати посилання, viewDidUnloadщоб уникнути звисаючий покажчик. Ось приклад висячої помилки вказівника, яку я зазнав:

UIViewController має поштовий індекс UITextField. Він використовує CLLocationManager для зворотного геокодування місцезнаходження користувача та встановлення поштового коду. Ось делегат зворотного виклику:

-(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];
}

Я виявив, що якщо я відмовився від цього погляду в потрібний час і не зняв з себе self.zip viewDidUnload, зворотний виклик делегата може викинути поганий виняток доступу на self.zip.text.


4
Я також розумію, що weakвластивості не потрібно вводити в живлення viewDidUnload. Але чому шаблон Apple для створення торгових точок включає в себе [self setMySubview:nil]?
Ян Мейєр

3
Чи є випадки реального світу, коли використання сильних / збережених для вашого IBOutlet може спричинити проблеми? Або це просто зайве збереження, що означає поганий стиль кодування, але це не вплине на ваш код?
Енцо Тран

1
Чи існує таке поняття, як надмірне збереження? Якщо є додаткове затримка, це призведе до неправильного підрахунку, і тому воно не буде звільнене якнайшвидше, оскільки на його рахунку збережеться додаткове утримання.
karlbecker_com

25

IBOutletповинні бути сильними, з причини виконання. Див. Довідку про розгорнуту дошку, сильний IBOutlet, док-станцію для iOS 9

Як пояснено в цьому пункті, випускні пункти для підвідних оглядів контролера перегляду можуть бути слабкими, оскільки ці підперегляди вже належать об'єкту верхнього рівня файлу nib. Однак, коли Outlet визначено як слабкий покажчик і встановлено покажчик, ARC викликає функцію виконання:

id objc_storeWeak(id *object, id value);

Це додає вказівник (об’єкт) до таблиці, використовуючи значення об’єкта як ключ. Ця таблиця називається слабкою. ARC використовує цю таблицю для зберігання всіх слабких покажчиків вашої програми. Тепер, коли значення об'єкта буде розміщено, ARC переправиться на слабку таблицю і встановить слабке посилання на нуль. Альтернативно, ARC може телефонувати:

void objc_destroyWeak(id * object)

Потім об'єкт не зареєстрований і objc_destroyWeak знову викликає:

objc_storeWeak(id *object, nil)

Таке ведення бухгалтерського обліку, пов’язане зі слабким посиланням, може зайняти в 2-3 рази довше, ніж випуск надійного посилання. Отже, слабке посилання вводить накладні витрати на час виконання, яких можна уникнути, просто визначивши торгові точки як сильні.

Що стосується Xcode 7, це говорить strong

Якщо ви переглядаєте сесію WWDC 2015 407 Впровадження дизайнів інтерфейсу в Builder інтерфейсу , це пропонує (стенограма з http://asciiwwdc.com/2015/sesions/407 )

І останній варіант, який я хочу зазначити - це тип зберігання, який може бути сильним або слабким.

Як правило, ви повинні зробити вашу розетку сильною, особливо якщо ви підключаєте розетку до підподання або обмеження, яке не завжди буде зберігатися ієрархією перегляду.

Єдиний раз, коли вам дійсно потрібно зробити розетку слабкою, це якщо у вас є власний перегляд, який посилається на щось резервне копіювання ієрархії перегляду, і взагалі це не рекомендується.

Тож я виберу сильний і натискаю на підключення, що генерує мою розетку.


1
Прекрасна відповідь, яка пояснює фактичну причину -wy-
micnguyen

Це добре, і все, але я не бачив витоків від розпізнавачів жестів, реалізованих у розгортці.
thibaut noah

1
Я не можу зрозуміти цю лінію. "Єдиний раз, коли вам дійсно потрібно зробити розетку слабкою, це якщо у вас є власний перегляд, який посилається на щось резервне копіювання ієрархії перегляду, і взагалі це не рекомендується." Будь-які приклади?
користувач1872384

Я порахував час денініту, який потребує слабкий і сильний, і це точно так само.
touti

Але швидше це так. Слабкі посилання швидше.
thesummersign

20

У розробці iOS завантаження NIB трохи відрізняється від розробки Mac.

У розробці Mac, як правило, IBOutlet є слабкою посиланням: якщо у вас є підклас NSViewController, зберігається лише перегляд верхнього рівня, і при обміні контролером усі його підгляди та розетки автоматично звільняються.

UiViewController використовує кодування ключових значень для встановлення торгових точок за допомогою чітких посилань. Таким чином, коли ви будете переносити UIViewController, вигляд зверху автоматично розміщуватиметься, але ви також повинні розмістити всі його розетки методом dealloc.

У цьому дописі з ранчо Big Nerd вони висвітлюють цю тему, а також пояснюють, чому використання чіткого посилання в IBOutlet не є вдалим вибором (навіть якщо це рекомендується Apple в цьому випадку).


16
Це пояснюється станом на 2009 рік. З ARC це значно змінилося.
Дафідд Вільямс

1
:( Посилання на Big Nerd Ranch мертве… але мені справді потрібно його прочитати. Хтось знає більше деталей про цю посаду, щоб я міг її знайти?
Motti Shneor

@MottiShneor не хвилюйся, це не велика справа, оскільки посилання було про часи до ARC і вже не актуально.
Сергій Грищйов

18

Тут я хочу зазначити одне, і це, незважаючи на те, що інженери Apple заявили у своєму власному відео WWDC 2015:

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

Apple продовжує змінювати свою думку з цього приводу, що говорить нам про те, що немає єдиної правильної відповіді на це питання. Щоб показати, що навіть інженери Apple розбиті на цю тему, погляньте на останній зразок коду Apple, і ви побачите, що деякі користуються слабкими, а деякі ні.

У цьому прикладі Apple Pay використовується слабкий: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewwintintEntrontlentlentlement

Як це стосується цього прикладу "Зображення на зображенні": https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166PAVPell_Pell_Pell_PetPell_PePPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPLAyer

Приклад Лістера: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html#//apple_ref/doc/uid/TP40014701-Lister_ListCell_swift-DontLinkElementID_57

Як, наприклад, приклад Основного розташування: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swintEsilftDell

Як вдає контролера приклад їх перегляду: 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

Як це робить приклад HomeKit: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html#//apple_ref/doc/uid/TP400150Ata_etSet_Cet_Cet_Cet_Cet_Cet_Cet_Cet

Усі вони повністю оновлені для iOS 9, і всі використовують слабкі торгові точки. З цього ми дізнаємось, що А. Питання не таке просте, як це роблять деякі люди. B. Apple неодноразово передумав, і C. Ви можете використовувати все, що робить вас щасливими :)

Особлива подяка Полю Хадсону (автору www.hackingwithsift.com), який надав мені роз'яснення та посилання на цю відповідь.

Сподіваюсь, це трохи прояснить тему!

Піклуватися.


Я десь перевіряв це питання і не знайшов конкретних відповідей. Оскільки вищенаведене посилання говорить про те, що і те, і інше, і взагалі йде з тим, що Xcode автозамагає.
subin272



5

Схоже, щось змінилося за ці роки, і тепер Apple рекомендує використовувати в цілому сильні. Докази їх сесії WWDC містяться в сесії 407 - Впровадження дизайнів інтерфейсу в Builder інтерфейсу і починається о 32:30. Моя примітка з того, що він говорить, є (майже, якщо не точно, цитую його):

  • Підключення до розетки в цілому повинні бути сильними, особливо якщо ми підключаємо підвид або обмеження, які не завжди зберігаються ієрархією перегляду

  • слабке підключення до розетки може знадобитися при створенні власних представлень, які мають деяку посилання на щось резервне копіювання в ієрархії подання, і взагалі це не рекомендується

В інших палатах вона повинна бути завжди сильною, доки деякі з наших власних представлень не створюють цикл збереження, а частина перегляду вгору в ієрархії подання

Редагувати:

Деякі можуть задати питання. Чи зберігає його з чіткими посиланнями, не створює цикл збереження, оскільки контролер кореневого перегляду, а власний вид зберігає посилання на нього? Або чому це змінилося сталося? Я думаю, що відповідь є раніше в цій розмові, коли вони описують, як створюються нитки з xib. Існує окрема ручка, створена для ВК та для перегляду. Я думаю, це може бути причиною того, що вони змінюють рекомендації. І все-таки було б непогано отримати глибше пояснення від Apple.


4

Я думаю, що найважливіша інформація така: Елементи в xib автоматично знаходяться в підглядах перегляду. Subviews - це NSArray. NSArray володіє своїми елементами. тощо мають сильні покажчики на них. Тому в більшості випадків ви не хочете створювати ще один сильний покажчик (IBOutlet)

І з ARC вам не потрібно нічого робити viewDidUnload

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.