Должен ли IBOutlets быть сильным или слабым в ARC?

485

Я разрабатываю исключительно для iOS 5, используя ARC. Если IBOutlet - UIView (и подклассы) - strong или weak?

Следующее:

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

Избавьтесь от всего этого:

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

Есть ли проблемы с этим? Шаблоны используют strong, а также автоматически создаваемые свойства, созданные при непосредственном подключении к заголовку из редактора "Interface Builder", но почему? UIViewController уже имеет ссылку strong на свой view, который сохраняет свои поднастройки.

  • 11
    Как примечание, IBOutletCollection() не должен быть weak , иначе он возвращается как nil .
  • 0
    Xcode 8.2.1 использует слабость при создании IBOutlets через конструктор интерфейса. Однако многие ответы здесь на SO советуют использовать сильные.
Показать ещё 1 комментарий
Теги:
cocoa-touch
interface-builder
automatic-ref-counting

11 ответов

166
Лучший ответ

Текущая рекомендуемая передовая практика от Apple заключается в том, чтобы IBOutlets были сильными, если только слабый не нужен, чтобы избежать цикла сохранения. Как упоминал Йоханнес, это было прокомментировано в разделе "Реализация интерфейса пользовательского интерфейса в интерфейсе" от WWDC 2015, где Apple Engineer сказал:

И последний параметр, который я хочу указать, - это тип хранилища, который может либо быть сильным или слабым. В общем, вы должны сделать свой выход сильный, особенно если вы подключаете розетку к подсмотру или к ограничение, которое не всегда будет сохранено в представлении иерархия. Единственный раз, когда вам действительно нужно сделать выход слабым, если у вас есть пользовательский вид, который ссылается на что-то резервное копирование представления иерархии и вообще не рекомендуется.

Я спросил об этом в Твиттере инженера команды IB, и он подтвердил, что сильный должен быть по умолчанию и что документы разработчика обновляются.

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

  • 29
    Это действительно так, или ответ с 300+ ответами правильный? Я заметил, что InterfaceBuilder по умолчанию использует слабую, когда вы перетаскиваете Ctrl из раскадровки в .h
  • 4
    Тот, у кого более 400 голосов, правильный, но устаревший. Поскольку iOS 6 viewDidUnload не вызывается, то нет никаких преимуществ для слабых точек.
Показать ещё 9 комментариев
440

ПРЕДУПРЕЖДЕНИЕ, НАНЕСЕННЫЙ ОТВЕТ: этот ответ не обновляется в соответствии с WWDC 2015, для правильного ответа см. принятый ответ (Daniel Hall) выше. Этот ответ останется для записи.


Обобщены из библиотека разработчиков:

С практической точки зрения, в iOS и OS X выходы должны быть определены как объявленные свойства. Розетки, как правило, должны быть слабыми, за исключением тех, которые принадлежат файловому владельцу, для объектов верхнего уровня в файле nib (или в iOS, сценае раскадровки), который должен быть сильным. Таким образом, создаваемые вами ролики обычно будут слабыми по умолчанию, потому что:

  • Розетки, которые вы создаете, например, в виде представлений в представлении диспетчера представлений или окне оконных контроллеров, представляют собой произвольные ссылки между объектами, которые не подразумевают право собственности.

  • Сильные розетки часто задаются классами framework (например, UIViewControllers view outlet или NSWindowControllers).

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
    
  • 10
    Как вы получили ссылку "библиотека разработчика", чтобы перейти к определенной части страницы документа Apple? Всякий раз, когда я делаю ссылку на документы Apple, они всегда указывают на верхнюю часть страницы (даже если интересующий контент находится на полпути вниз по странице). Благодарю.
  • 68
    Я скопировал ссылку с панели навигации слева. : D
Показать ещё 16 комментариев
46

В то время как в документации рекомендуется использовать weak для свойств для subviews, так как iOS 6, похоже, будет использовать strong (классификатор прав собственности по умолчанию).. Это вызвано изменением UIViewController, что представления больше не выгружаются.

  • До того, как iOS 6, если вы сохранили сильные ссылки на subviews в представлении контроллера, если основное представление диспетчера представлений получило разгрузку, они будут удерживаться на подзапросах, пока вокруг будет контроллер вида.
  • Начиная с iOS 6, представления больше не выгружаются, но загружаются один раз, а затем приклеиваются до тех пор, пока их контроллер находится. Такие сильные свойства не будут иметь значения. Они также не будут создавать сильные ссылочные циклы, поскольку они указывают на сильный опорный граф.

Тем не менее, я разорван между использованием

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

и

@property (nonatomic) IBOutlet UIButton *button;

в iOS 6 и после:

  • Использование weak четко указывает, что контроллер не хочет владеть кнопкой.

  • Но опускать weak не вредит в iOS 6 без разгрузки изображения и короче. Некоторые могут указать, что это также быстрее, но мне еще предстоит встретить приложение, которое слишком медленно из-за weak IBOutlet s.

  • Не использовать weak можно воспринимать как ошибку.

Итог: с iOS 6 мы больше не можем ошибаться, пока мы не используем разгрузку вида. Время вечеринки.;)

  • 0
    Это правда, но вы все еще можете разгрузить вид самостоятельно. В этом случае вы должны будете установить все ваши розетки на nil вручную.
  • 0
    PS: weak это немного дешевле в ARM64: D
Показать ещё 9 комментариев
35

Я не вижу никаких проблем с этим. Pre-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];
}

Я обнаружил, что если я отклонил это представление в нужное время и не сделал nil self.zip в viewDidUnload, обратный вызов делегата мог бы вызвать исключение плохого доступа в self.zip.text.

  • 4
    Насколько я понимаю, weak свойства не нужно обнулять в viewDidUnload . Но почему шаблон Apple для создания торговых точек включает [self setMySubview:nil] ?
  • 0
    @ Ян, мне интересно то же самое
Показать ещё 2 комментария
21

В iOS-разработке загрузка NIB немного отличается от разработки Mac.

В Mac-разработке IBOutlet обычно является слабой ссылкой: если у вас есть подкласс NSViewController, то будет сохранено только представление верхнего уровня, и когда вы отключите контроллер, все его подпункты и выходы будут освобождены автоматически.

UiViewController использует Key Value Coding для установки розеток с использованием сильных ссылок. Поэтому, когда вы отключите свой UIViewController, верхний вид автоматически будет освобожден, но вы также должны освободить все его выходы в методе dealloc.

В этом сообщении от Big Nerd Ranch, они охватывают эту тему, а также объясняют, почему использование сильной ссылки в IBOutlet не является хорошим выбором (даже если это рекомендуется Apple в этом случае).

  • 16
    Это объясняет это по состоянию на 2009 год. С ARC это значительно изменилось.
  • 1
    :( Ссылка на Big Nerd Ranch мертва… но мне действительно нужно ее прочитать. Кто-нибудь знает больше подробностей об этом посте, чтобы я мог его найти?
Показать ещё 1 комментарий
9

Одна вещь, которую я хочу отметить здесь, и это, несмотря на то, что инженеры Apple заявили в своем собственном видео WWDC 2015:

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

Apple постоянно меняет свое мнение по этому вопросу, что говорит нам о том, что на этот вопрос нет единого правильного ответа. Чтобы показать, что даже инженеры Apple разделены по этому вопросу, взгляните на Apple в последнее время пример кода, и вы увидите, что некоторые люди используют слабый, а некоторые нет.

В этом примере Apple PayPack используется слабый: https://developer.apple.com/library/ios/samplecode/Emporium/Listings/Emporium_ProductTableViewController_swift.html#//apple_ref/doc/uid/TP40016175-Emporium_ProductTableViewController_swift-DontLinkElementID_8

Как и в примере с изображением в картинке: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-AVFoundationPiPPlayer_PlayerViewController_swift-DontLinkElementID_4

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

Как и пример Core Location: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html#//apple_ref/doc/uid/TP40016176-Potloc_PotlocViewController_swift-DontLinkElementID_6

Как и пример предварительного просмотра контроллера: 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/TP40015048-HMCatalog_Homes_Action_Sets_ActionSetViewController_swift-DontLinkElementID_23

Все они полностью обновлены для iOS 9, и все используют слабые розетки. Из этого мы узнаем, что А. Проблема не такая простая, как некоторые люди делают это. B. Apple неоднократно передумала, и C. Вы можете использовать то, что делает вас счастливым:)

Особая благодарность Полю Хадсону (автор www.hackingwithsift.com), который дал мне разъяснения и ссылки на этот ответ.

Надеюсь, это немного улучшит тему!

Позаботьтесь.

  • 0
    Я проверял эту проблему в течение некоторого времени и не нашел никаких конкретных ответов. Так как приведенная выше ссылка предполагает, что оба в порядке и в целом идут с тем, что Xcode AutoSuggests.
8

IBOutlet должен быть сильным, по соображениям производительности. См. Справочник раскадровки, Сильный IBOutlet, Сцена Dock в iOS 9

Как поясняется в этом параграфе, выходы в подпункты представления просмотр контроллеров может быть слабым, поскольку эти подзадачи уже принадлежащий объекту верхнего уровня файла nib. Однако, когда выход определяется как слабый указатель, и указатель установлен, ARC вызывает функция выполнения:

id objc_storeWeak(id *object, id value);

Это добавляет указатель (объект) в таблицу, используя значение объекта в качестве ключа. Эта таблица называемый слабым столом. ARC использует эту таблицу для хранения всех слабые указатели на ваше приложение. Теперь, когда значение объекта deallocated, ARC будет перебирать слабую таблицу и устанавливать слабые ссылка на ноль. Альтернативно, ARC может вызывать:

void objc_destroyWeak(id * object)

Затем объект незарегистрированный и objc_destroyWeak снова вызывает:

objc_storeWeak(id *object, nil)

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

Как и в Xcode 7, предлагается strong

Изображение 5325

Если вы смотрите сеанс WWDC 2015 407 Внедрение дизайна пользовательского интерфейса в построителе интерфейсов, он предлагает (стенограмма из http://asciiwwdc.com/2015/sessions/407)

И последний параметр, который я хочу указать, - это тип хранилища, который может быть сильным или слабым.

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

Единственный раз, когда вам действительно нужно сделать выход слабым, - это если у вас есть пользовательский вид, который ссылается на что-то, поддерживающую иерархию представлений, и вообще не рекомендуется.

Итак, я собираюсь выбрать сильный, и я нажму кнопку connect, которая будет генерировать мою розетку.

  • 1
    Отличный ответ, который объясняет истинную причину
  • 0
    Это хорошо, и все, но я видел утечки из распознавателей жестов, реализованных в раскадровке.
Показать ещё 2 комментария
8

Из WWDC 2015 существует сессия на Внедрение дизайна пользовательского интерфейса в построителе интерфейса. Около 32 минут он говорит, что вы всегда хотите сделать свой @IBOutlet сильный.

  • 0
    Интересно. Я думаю, это изменилось, когда выгрузка представления была удалена?
6

Знайте, IBOutletCollection должен быть @property (strong, nonatomic).

  • 3
    Почему бы не copy как NSArray ?
5

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

  • выходные соединения вообще должны быть сильными, особенно если мы подключаем subview или ограничение, которое не всегда сохраняется иерархия представлений

  • может возникнуть необходимость подключения к слабым розеткам при создании пользовательских представлений, содержащих некоторую ссылку на что-то, что находится в иерархии представлений и вообще не рекомендуется

В других палатах он всегда должен быть сильным, пока некоторые из наших пользовательских представлений не создают цикл сохранения с некоторым представлением в иерархии представлений

EDIT:

Некоторые могут задать вопрос. Поддерживает ли он сильную ссылку, не создает цикл сохранения, поскольку контроллер корневого представления и вид собственности сохраняют ссылку на него? Или почему это изменилось? Я думаю, что ответ более ранний в этом разговоре, когда они описывают, как создаются новички из xib. Существует отдельный наконечник, созданный для VC и для представления. Я думаю, что это может быть причиной того, что они меняют рекомендации. Тем не менее было бы неплохо получить более глубокое объяснение от Apple.

4

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

И с ARC вам не нужно ничего делать в viewDidUnload

Ещё вопросы

Сообщество Overcoder
Наверх
Меню