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

Я разрабатываю исключительно для iOS 5 с использованием ARC. Должен IBOutletс UIViewS (и подклассы) быть strong или же weak?

Следующие:

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

Избавился бы от всего этого:

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

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

11 ответов

Решение

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

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

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

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

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


Суммировано из библиотеки разработчика:

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

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

  • Сильные выходы часто определяются классами каркаса (например, выходом представления UIViewController или выходом окна NSWindowController).

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

Хотя документация рекомендует использовать 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 без выгрузки вида, и короче. Некоторые могут указать, что это также быстрее, но я еще не сталкивался с приложением, которое слишком медленное из-за weakIBOutlet s.

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

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

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

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

IBOutlet должен быть сильным по причине производительности. См. Storyboard Reference, Сильный IBOutlet, Scene Dock в iOS 9

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

id objc_storeWeak(id *object, id value);

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

void objc_destroyWeak(id * object)

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

objc_storeWeak(id *object, nil)

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

Начиная с Xcode 7, он предлагает strong

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

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

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

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

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

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

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

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

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

Здесь я хотел бы отметить одну вещь: несмотря на то, что инженеры 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

Как и этот пример "картинка в картинке": https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html

Как и в примере с Lister: https://developer.apple.com/library/ios/samplecode/Lister/Listings/Lister_ListCell_swift.html

Как и в примере с основным местоположением: https://developer.apple.com/library/ios/samplecode/PotLoc/Listings/Potloc_PotlocViewController_swift.html

Как делает вид контроллера при предварительном просмотре пример: https://developer.apple.com/library/ios/samplecode/ViewControllerPreviews/Listings/Projects_PreviewUsingDelegate_PreviewUsingDelegate_DetailViewController_swift.html

Как и пример HomeKit: https://developer.apple.com/library/ios/samplecode/HomeKitCatalog/Listings/HMCatalog_Homes_Action_Sets_ActionSetViewController_swift.html

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

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

Я надеюсь, что это проясняет тему немного лучше!

Береги себя.

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

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

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

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

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

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

РЕДАКТИРОВАТЬ:

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

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

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

Другие вопросы по тегам