Почему в примере кода iPhone используется так много промежуточных переменных? - PullRequest
4 голосов
/ 12 августа 2011

В настоящее время я работаю над Apress "Beginning iPhone 3 Development". Стандарт, который они используют в своих примерах, подобен следующему коду:

- (void)viewDidLoad {
    BlueViewController *blueController = [[BlueViewController alloc] 
                                         initWithNibName:@"BlueView" bundle:nil];
    self.blueViewController = blueController;
    [self.view insertSubview:blueController.view atIndex:0];
    [blueController release];
}

8.14.11 ОБНОВЛЕНИЕ (дополнительная информация)
blueViewController объявлен следующим образом:

@property (retain, nonatomic) BlueViewController *blueViewController;

Всякий раз, когда они выполняют alloc, они помещают его в некоторую временную переменную (здесь blueController), затем они присваивают его, а затем освобождают. Эта временная переменная мне кажется излишней.
Я упростил код следующим образом:

- (void)viewDidLoad {
    self.blueViewController = [[BlueViewController alloc] 
                              initWithNibName:@"BlueView" bundle:nil];
    [self.view insertSubview:blueViewController.view atIndex:0];
}

- (void)dealloc {
    [blueViewController release];
    [super dealloc];
}

Мой модифицированный код работал точно так же в симуляторе iPhone. Теперь я знаю правило, что если вы выделяете что-то, вам нужно его освободить. И я освещаю это в моем dealloc методе. Но есть ли какое-то преимущество в том, что выпуск непосредственно в ViewDidLoad (функция, где вызывался alloc)? Или одинаково нормально иметь release в вашем dealloc методе, подобном этому?
Спасибо за любую помощь,
-j

Ответы [ 3 ]

8 голосов
/ 12 августа 2011

Если blueViewController является свойством retain, временная переменная не является лишней . Ваше упрощение создает утечку памяти. Это утверждение из второго фрагмента утечки:

self.blueViewController = [[BlueViewController alloc] 
                          initWithNibName:@"BlueView" bundle:nil];

С точки зрения владения, вы являетесь владельцем объекта, возвращаемого alloc-init, а затем средство доступа к свойству снова претендует на владение объектом, в результате чего объект переоценивается .

Использование временной переменной решает эту проблему. Другой вариант - использовать autorelease:

self.blueViewController = [[[BlueViewController alloc] 
                          initWithNibName:@"BlueView" bundle:nil] autorelease];

Обратите внимание, что после этого оператора вы фактически владеете объектом и должны освободить его в dealloc.


Вы не упомянули, как объявлено свойство blueViewController. В любом случае, независимо от того, какой семантикой является сеттер (retain, copy, assign), это утверждение неверно. Я уже объяснил наиболее вероятный сценарий: retain. Давайте посмотрим на две другие возможности (не учитывая, имеют ли они смысл):

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

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

3 голосов
/ 12 августа 2011
@property (retain) MyCLass *obj;

MyClass *tmpObj = [[MyClass alloc] init];
self.obj = tmpObj;
[tmpObj release];

В первой строке вы получаете право владения один раз через alloc. Затем во 2-й строке вы снова получаете право собственности, поскольку собственность сохраняется. В 3-й строке вы освобождаете право собственности, полученное через alloc. Теперь у вас есть одно право собственности через оставленное имущество, которое вы можете освободить в будущем, может быть в dealloc.

Теперь рассмотрим, что произойдет, если вы удалите tmpObj.

self.obj = [[MyClass alloc] init];

В этой строке вы получаете право собственности дважды: один раз через alloc и один раз через собственность. Теперь [obj release] одного раза недостаточно. Чтобы избежать утечки, вам нужно дважды ее выпустить, и, естественно, повторное высвобождение крайне вредно и может стать источником дальнейшей утечки памяти. Если вы сделаете еще один звонок на self.obj = anotherObj, значит, у вас будет утечка старого. Чтобы избежать этого, вам нужен этот временный указатель.

0 голосов
/ 12 августа 2011

Есть две причины, по которым я могу думать о макушке головы;Первый, более конкретный для примера, заключается в том, что вы часто будете видеть похожие методы, в которых blueController выделяется и инициализируется, затем проверяется на достоверность перед тем, как фактически назначаться для ивара self.Следование этой схеме в каждом таком методе облегчит вам выполнение тестов между созданием объекта и его назначением, если вы поймете, что это должно произойти.Насколько мне известно, если такой посредник действительно остается излишним, компилятор должен оптимизировать его.

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

...