Подтвердить что ты не робот

Что произойдет, если я не сохраню IBOutlet?

Если я это сделаю:

@interface RegisterController : UIViewController <UITextFieldDelegate>
{
    IBOutlet UITextField *usernameField;
}

вместо этого:

@interface RegisterController : UIViewController <UITextFieldDelegate>
{
    UITextField *usernameField;
}
@property (nonatomic, retain) IBOutlet UITextField *usernameField;

Будет ли что-то плохое? Я знаю во втором случае, поле сохраняется, но отличается ли это от того, что nib владеет этим полем? Будет ли поле уходить без сохранения? и при каких обстоятельствах? Код в первом случае работает, задавался вопросом, является ли это проблемой или нет с точки зрения управления памятью.

4b9b3361

Ответ 1

Для ясности и согласованности рекомендуется объявлять свойства для всех ваших IBOutlets. Подробности описаны в Руководство по программированию памяти. Основной смысл заключается в том, что когда ваши объекты NIB будут разблокированы, код загрузки nib будет проходить и установить все IBOutlets, используя setValue: forKey:. Когда вы объявляете поведение управления памятью в свойстве, нет никакой тайны относительно того, что происходит. Если представление выгружается, но вы использовали свойство, которое было объявлено как сохраненное, вы все равно имеете действительную ссылку на свое текстовое поле.

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

Я собираюсь сделать некоторые предположения относительно контекста, в котором вы работаете - я предполагаю, что UITextField выше является подвидью другого представления, которое контролируется UIViewController. Я предполагаю, что в какой-то момент представление отключено от экрана (возможно, оно используется в контексте UINavigationController), и что в какой-то момент ваше приложение получает предупреждение о памяти.

Итак, пусть ваш подкласс UIViewController должен получить доступ к его представлению, чтобы отобразить его на экране. На этом этапе файл nib будет загружен, и каждое свойство IBOutlet будет установлено кодом загрузки nib, используя setValue: forKey:. Здесь важны вид верхнего уровня, который будет установлен в свойство представления UIViewController (которое сохранит это представление верхнего уровня) и ваш UITextField, который также будет сохранен. Если он просто установлен, на нем будет сохраняться код загрузки nib, иначе свойство сохранит его. UITextField также будет подчинен UIView верхнего уровня, поэтому он будет иметь дополнительную удержание на нем, находясь в массиве subviews верхнего уровня, поэтому на этом этапе текстовое поле было сохранено дважды.

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

Теперь скажем, что в стеке UINavigation Controller помещается другой контроллер представления, так что это представление больше не находится на переднем плане. В случае предупреждения о памяти вид этого внешнего монитора будет выгружен. На этом этапе свойство view верхнего уровня UIView будет отменено, оно будет освобождено и освобождено.

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

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

Интересным моментом является то, что, поскольку UITextField дополнительно сохраняется через свойство, вы вряд ли захотите его сохранить в случае предупреждения о памяти. По этой причине вы должны отключить свойство в методе - [UIViewController viewDidUnload]. Это избавит вас от окончательной версии на UITextField и освободит ее, как предполагалось. Если вы используете это свойство, вы должны помнить о его явном выпуске. Хотя эти два действия функционально эквивалентны, намерение отличается.

Если вместо замены текстового поля вы решили удалить его из представления, вы, возможно, уже удалили его из иерархии представлений и установили свойство в nil или выпустили текстовое поле. Хотя в этом случае можно написать правильную программу, легко сделать ошибку перевыпуска текстового поля в методе viewDidUnload. Сверхвыдача объекта является ошибкой, вызывающей сбои; установка свойства, которое уже равно nil, равно nil, не является.

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

Кроме того, стоит отметить, что поведение управления памятью отличается от Mac OS X на рабочем столе. На рабочем столе установка IBOutlet без сеттера не сохраняет переменную экземпляра; но снова использует установщик, если он доступен.

Ответ 2

Объявление чего-то IBOutlet, с точки зрения управления памятью, ничего не делает (IBOutlet буквально #defined как ничто). Единственная причина включения IBOutlet в объявление - это если вы собираетесь подключить его в Interface Builder (для чего предназначена декларация IBOutlet, подсказка для IB).

Теперь единственной причиной создания @property для переменной экземпляра является то, что вы намерены назначить их программно. Если вы этого не сделаете (то есть вы только настраиваете свой интерфейс в IB), неважно, создаете ли вы свойство или нет. Нет причин, ИМО.

Вернемся к вашему вопросу. Если вы устанавливаете этот ivar (имя пользователяField) в IB, не беспокойтесь об этом свойстве, это ничего не повлияет. Если вы создадите свойство для usernameField (потому что вы его программно создаете), определенно сделайте для него свойство, и абсолютно НЕ сделайте свойство, если это так.

Ответ 3

На самом деле есть две модели:

СТАРЫЙ МОДЕЛЬ

Эта модель была моделью до Objective-C 2.0 и унаследована от Mac OS X. Она по-прежнему работает, но вы не должны объявлять свойства для изменения ivars. То есть:

@interface StrokeWidthController : UIViewController {
    IBOutlet UISlider* slider;
    IBOutlet UILabel* label;
    IBOutlet StrokeDemoView* strokeDemoView;
    CGFloat strokeWidth;
}
@property (assign, nonatomic) CGFloat strokeWidth;
- (IBAction)takeIntValueFrom:(id)sender;
@end

В этой модели вы не сохраняете IBOutlet ivars, но вы должны их освободить. То есть:

- (void)dealloc {
    [slider release];
    [label release];
    [strokeDemoView release];
    [super dealloc];
}

НОВАЯ МОДЕЛЬ

Вы должны объявить свойства для переменных IBOutlet:

@interface StrokeWidthController : UIViewController {
    IBOutlet UISlider* slider;
    IBOutlet UILabel* label;
    IBOutlet StrokeDemoView* strokeDemoView;
    CGFloat strokeWidth;
}
@property (retain, nonatomic) UISlider* slider;
@property (retain, nonatomic) UILabel* label;
@property (retain, nonatomic) StrokeDemoView* strokeDemoView;
@property (assign, nonatomic) CGFloat strokeWidth;
- (IBAction)takeIntValueFrom:(id)sender;
@end

Кроме того, вы должны освободить переменные в dealloc:

- (void)dealloc {
    self.slider = nil;
    self.label = nil;
    self.strokeDemoView = nil;
    [super dealloc];
}

Далее, на не-хрупких платформах вы можете удалить ivars:

@interface StrokeWidthController : UIViewController {
    CGFloat strokeWidth;
}
@property (retain, nonatomic) IBOutlet UISlider* slider;
@property (retain, nonatomic) IBOutlet UILabel* label;
@property (retain, nonatomic) IBOutlet StrokeDemoView* strokeDemoView;
@property (assign, nonatomic) CGFloat strokeWidth;
- (IBAction)takeIntValueFrom:(id)sender;
@end

ВЕЧЕРЬ

В обоих случаях выходы устанавливаются вызовом setValue: forKey:. Внутренняя среда выполнения (в частности, _decodeObjectBinary) проверяет, существует ли метод установки. Если он не существует (существует только ivar), он отправляет дополнительное удержание в ivar. По этой причине вы не должны сохранять IBOutlet, если нет метода setter.

Ответ 4

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

В обоих случаях вам все равно нужно освободить и установить в ноль IBOutlet в методах dealloc или viewDidUnload.

IBOutlet указывает на объект, созданный в XIB файле. Этот объект принадлежит объекту File Owner файла XIB (обычно это контроллер представления, в котором объявлен IBOutlet.

Поскольку объект создается в результате загрузки XIB, он сохраняет счет 1 и принадлежит файловому владельцу, как указано выше. Это означает, что владелец файла отвечает за его освобождение, когда он освобожден.

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

Ответ 5

Объекты в файле nib создаются с сохранением числа 1 и затем автоматически сохраняются. Поскольку он восстанавливает объект иерархии, UIKit восстанавливает соединения между объектами с помощью setValue: forKey:, который использует доступный метод setter или сохраняет объект по умолчанию, если не доступен метод setter. Это означает, что любой объект, для которого у вас есть выход, остается в силе. Однако если какие-либо объекты верхнего уровня, которые вы не храните в торговых точках, вы должны сохранить либо массив, возвращаемый методом loadNibNamed: owner: options: или объекты внутри массива, чтобы предотвратить преждевременное освобождение этих объектов.

Ответ 6

Хорошо, во втором случае вы добавляете метод getter/setter для этого конкретного IBOutlet. Каждый раз, когда вы добавляете метод getter/setter, вы (почти всегда) хотите, чтобы он сохранялся для проблем управления памятью. Я думаю, что лучший способ задать вам вопрос был бы таким:

@interface RegisterController : UIViewController <UITextFieldDelegate>
{
IBOutlet UITextField *usernameField;
}
@property (nonatomic) IBOutlet UITextField *usernameField;

или

@interface RegisterController : UIViewController <UITextFieldDelegate>
{
IBOutlet UITextField *usernameField;
}
@property (nonatomic, retain) IBOutlet UITextField *usernameField;

В таком случае, да, вам нужно будет добавить сохранение, так как это повлияет на управление памятью. Несмотря на то, что он может не иметь каких-либо эффектов, если вы программно добавляете и удаляете IBOutlet, вы можете столкнуться с проблемами.

Как правило: всегда добавляйте @property (с сохранением) всякий раз, когда у вас есть IBOutlet.