Это более чем сложно. Я суммировал мой ответ в 5 частях:
- Создание пользовательского
init
метода, который возвращает другой объект
- ВНИМАНИЕ: остерегайтесь несанкционированного доступа к памяти!
- Как правильно передать право собственности на кнопку в родительский вид
- Конкретные ответы на конкретные вопросы
- Предложение по улучшению
Часть 1 : Создание пользовательского метода init
, который возвращает другой объект:
Это пример очень особого случая, а именно, что объект, возвращаемый из -initWithTitle:frame:
, является , а не тем же «объектом», который отправил сообщение в первую очередь.
Обычно, процесс идет так:
instance = [Class alloc];
[instance init];
...
[instance release];
Конечно, вызовы alloc и init обычно группируются в одну строку кода. Ключевым моментом, на который следует обратить внимание, является то, что «объект» (не более чем выделенный блок памяти в этой точке), который получает вызов init, уже был выделен. Если вы возвращаете другой объект (как в вашем примере), вы несете ответственность за освобождение этого исходного блока памяти.
Следующим шагом будет возвращение нового объекта с надлежащим счетом удержания. Так как вы используете фабричный метод (+buttonWithType:
), полученный объект был автоматически освобожден, и вы должны сохранить его, чтобы установить правильное количество сохранений.
Редактировать: Правильный метод -init
должен явно проверить, чтобы убедиться, что он работает с правильно инициализированным объектом до того, как он сделает что-то еще с этот объект. Этот тест отсутствовал в моем ответе, но присутствовал в ответе bbum .
Вот как должен выглядеть ваш метод init:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
if (self == nil) { return nil; }
[self retain]; // set the proper retain count
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
Часть 2: ВНИМАНИЕ: остерегайтесь несанкционированного доступа к памяти!
Если вы выделяете экземпляр CustomButton
, а затем заменяете его экземпляром UIButton
, вы можете легко вызвать очень незначительные ошибки памяти. Допустим, у CustomButton
есть несколько иваров:
@class CustomButton : UIButton
{
int someVar;
int someOtherVar;
}
...
@end;
Теперь, когда вы заменяете выделенный CustomButton
экземпляром UIButton
в своем пользовательском методе init...
, вы возвращаете блок памяти, который слишком мал, чтобы содержать CustomButton
, но ваш код будет продолжать обрабатывать этот блок кода, как если бы он был полноразмерным CustomButton
. Ой
Например, следующий код очень, очень плохо:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self retain]; // set the proper retain count
someOtherVar = 10; // danger, Will Robinson!
return self;
}
Часть 3 : Как правильно передать право собственности на кнопку в родительский вид:
Что касается метода dealloc
вашего контроллера представления, вам придется вызвать [myButton release]
, если вы инициализировали кнопку, как показано на рисунке. Это должно следовать правилу, согласно которому вы должны освобождать все, что вы выделяете, сохраняете или копируете. Лучший способ справиться с этой проблемой - позволить представлению контроллера стать владельцем этой кнопки (что происходит автоматически при добавлении кнопки в качестве подпредставления):
myButton = [[CustomButton alloc] initWithTitle:@"Title"
frame:someFrame]; // RC = 1
[self.view addSubview:myButton]; // RC = 2
[myButton release]; // RC = 1
Теперь вам больше не нужно беспокоиться о том, чтобы снова отпустить эту кнопку. Представление владеет им и освобождает его, когда само представление освобождается.
Часть 4 : Конкретные ответы на конкретные вопросы:
В: Как я понимаю, myButton на самом деле не сохраняется, хотя я вызывал его с помощью alloc, потому что в моем подклассе я создал кнопку автоматического выпуска (используя buttonWithType:).
Правильно.
В: В dealloc, означает ли это, что при вызове dealloc суперпредставление отпускает кнопку, и ее счетчик удержаний уменьшается до 1? Кнопка еще не была выпущена автоматически?
Также правильно.
В: Или мне нужно, чтобы счет удержания уменьшился до нуля после вызова [super dealloc]?
Сортировка :)
Количество сохраняемых записей может уменьшаться или уменьшаться до нуля в тот момент, когда вы регистрируете его. У автоматически выпущенных объектов все еще может быть счетчик хранения, равный единице, так как они фактически принадлежат пулу автоматического выпуска для текущего цикла выполнения. В этом отношении само представление может все еще принадлежать окну, которое еще не было выпущено. Единственное, о чем вам действительно нужно беспокоиться - это сбалансировать собственное управление памятью. Подробнее см. Руководство по управлению памятью Apple . С точки зрения вашего viewController, вы выделили кнопку один раз, поэтому вы должны отпустить ее ровно один раз. Когда дело доходит до вашего пользовательского метода init...
, все становится немного сложнее, но принцип тот же. Блок памяти был выделен, поэтому он должен быть освобожден (часть 1), и (часть 2) init должен вернуть объект с сохраненным счетчиком единиц (для правильного освобождения позже).
Часть 5 : предложение по улучшению:
Вы можете избежать большей части беспорядка пользовательского инициализатора, просто создав собственный метод фабрики в том же духе, что и UIButton
:
+ (id)buttonWithTitle:(NSString *)title frame:(CGRect)btnFrame {
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
button.frame = btnFrame;
return button;
}
Обратите внимание, что этот подход все еще может привести к ошибкам доступа к памяти, как указано в части 2