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

551

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

Последующий:

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

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

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

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

hypercrypt
источник
11
Как примечание, IBOutletCollection()не должно быть weak, в противном случае он возвращается как nil.
Ох,
Xcode 8.2.1 использует слабость при создании IBOutlets через конструктор интерфейса. Однако многие ответы здесь на SO советуют использовать сильные.
neoneye
1
@neoneye Я только что попробовал перетащить xcode 8.3.2 из раскадровки в файл swift и по умолчаниюstrong
CupawnTae

Ответы:

252

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

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

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

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

Даниэль Холл
источник
33
Это действительно так или же ответ с 300+ ответами правильный? Я заметил, что InterfaceBuilder по умолчанию использует слабую, когда вы перетаскиваете Ctrl из раскадровки в .h
Арунабх Дас
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, для правильного ответа обратитесь к принятому ответу (Даниэль Холл) выше. Этот ответ останется для записи.


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

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

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

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

    @property (weak) IBOutlet MyView *viewContainerSubview;
    @property (strong) IBOutlet MyOtherClass *topLevelObject;
Александр Акерс
источник
10
Как вы получили ссылку "библиотека разработчика", чтобы перейти к определенной части страницы документа Apple? Всякий раз, когда я делаю ссылку на документацию Apple, она всегда указывает на верхнюю часть страницы (даже если интересующий контент находится на полпути вниз по странице). Спасибо.
BearMountain
68
Я скопировал ссылку с панели навигации слева. : D
Александр Акерс
27
Что означает «за исключением тех, которые принадлежат Владельцу Файла объектам верхнего уровня в файле пера (или, в iOS, сцене раскадровки)»?
Ван Ду Тран
16
@VanDuTran - это означает, что объекты в NIB находятся на корневом уровне, то есть, скажем, вы создали в нем другое представление, которое не является прямым представлением основного представления, тогда оно должно иметь сильную ссылку.
mattjgalloway
6
Верхний уровень означает, что когда вы смотрите на перо, объект появляется в списке слева. Почти все перья имеют UIView в них - это может быть единственный объект верхнего уровня. Если вы добавляете другие элементы, и они отображаются в списке, они являются «объектами верхнего уровня»
Дэвид Х
50

В то время как документация рекомендует использовать weakсвойства on для подпредставлений, с 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вручную.
hypercrypt
PS: weakнемного дешевле в ARM64: D
гиперкрипт
Правильно, если вы реализуете разгрузку вида, weakсвойства или __weakпеременные экземпляра - это путь. Я просто хотел отметить, что здесь меньше ошибок. Что касается weakтого, чтобы быть дешевле на arm64, я даже не видел реальной проблемы с производительностью с weak IBOutlets на armv7. :)
Таммо Фриз
В этом случае также strongимеет смысл. strongвредно, только если вы используете разгрузку вида - но кто это делает в наши дни? :)
Таммо Фриз
2
@Rocotilos Первый iPhone имел очень ограниченную оперативную память. Если я правильно помню, 128 МБ, оставляя около 10 МБ для активного приложения. Наличие небольшого объема памяти имело решающее значение, поэтому была разгрузка просмотра. Это изменилось, поскольку теперь у нас все больше и больше оперативной памяти, и Apple оптимизировала UIViews в 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

Как объяснено в этом параграфе, выходы для подпредставлений представления контроллера представления могут быть слабыми, потому что эти подпредставления уже принадлежат объекту верхнего уровня файла пера. Однако, когда 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 )

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

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

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

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

onmyway133
источник
1
Отличный ответ, который объясняет фактическую причину
почему-
Это хорошо, и все, но я видел утечки из распознавателей жестов, реализованных в раскадровке.
Тибо Ноа
1
Я не могу понять эту строку. «Единственный раз, когда вам действительно нужно сделать выход слабым, - это если у вас есть пользовательское представление, которое ссылается на что-то, поддерживающее иерархию представления, и в целом это не рекомендуется». Есть примеры?
user1872384
Я рассчитал время деинита, которое берет слабый и сильный, и оно точно такое же.
Тути
Но в скором времени это скорее так. Слабые ссылки быстрее.
thesummersign
20

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

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

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

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

Giuseppe
источник
16
Это объясняет это по состоянию на 2009 год. С ARC это значительно изменилось.
Дафидд Уильямс
1
:( ссылка на Ранчо Большого Ботаника мертва… но мне действительно нужно прочитать ее. Кто-нибудь знает больше подробностей об этом посте, чтобы я мог его найти?
Мотти Шнеор
@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_ProductTableViewController_swE

Как и этот пример «картинка в картинке»: https://developer.apple.com/library/ios/samplecode/AVFoundationPiPPlayer/Listings/AVFoundationPiPPlayer_PlayerViewController_swift.html#//apple_ref/doc/uid/TP40016166-onterL_PlayerPlayPlayPlayPlayPlayPlayD

Как и пример Листера: 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-DontLink

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

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

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

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

Береги себя.

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

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

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

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

landonandrey
источник
3
Почему не copyкак то NSArray?
Гиперкрипт
5

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

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

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

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

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

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

Джулиан Крол
источник
4

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

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

kraag22
источник