Это более чем сложно. Я кратко мой ответ на 5 частей:
- Создание пользовательского
init
метод, который возвращает другой объект
- ПРЕДУПРЕЖДЕНИЕ: остерегайтесь незаконного доступа к памяти!
- Как правильно передать право собственности на кнопке своего родительского вида
- Конкретных ответов на конкретные вопросы
- предложения по улучшению
Часть 1: Создание пользовательского init
метода, возвращает другой объект:
Это пример особого случая, а именно: obj ect, возвращаемый с -initWithTitle:frame:
, равен не тот же «объект», которому было отправлено сообщение в первую очередь.
Обычно говоря, процесс выглядит следующим образом:
instance = [Class alloc];
[instance init];
...
[instance release];
Конечно, Alloc и Инициализационные вызовы обычно группируются в одну строку кода. Ключевое замечание здесь заключается в том, что «объект» (не более, чем выделенный блок памяти в этой точке), который получает вызов init, уже выделен. Если вы вернете другой объект (как в вашем примере), вы несете ответственность за освобождение этого исходного блока памяти.
Следующим шагом будет возврат нового объекта с надлежащим количеством удержаний.Поскольку вы используете заводский метод (+buttonWithType:
), результирующий объект был автореализован, и вы должны сохранить его, чтобы установить правильное количество удержания.
Edit: Правильный -init
метод должен явно проверить, чтобы убедиться, что он работает с правильно инициализирован объект перед тем он делает что-нибудь еще с этим объектом. Этот тест отсутствовал в моем ответе, но присутствует в bbum's answer.
Вот как ваш метод инициализации должен выглядеть:
- (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: Конкретные ответы на конкретные вопросы:
Q: Как я понимаю, MyButton на самом деле не сохраняется, даже если я его вызвал с помощью Alloc, потому что в моем подклассу Я создал кнопку автозапуска (используя buttonWithType :).
Исправить.
В: В dealloc это означает, что, когда dealloc называется супервизором, отпускает кнопку, и ее значение удержания уменьшается до 1? Кнопка еще не автореализована?
Также правильно.
Вопрос: Или мне нужно получить, чтобы сохранить отсчет до нуля после вызова [super dealloc]?
Сортировка :)
Счет за хранение может или не упасть до нуля в момент его регистрации. Автореализованные объекты могут по-прежнему иметь счетчик удержания одного, поскольку они фактически принадлежат пулу автозаполнения для текущего цикла цикла. В этом отношении само представление все равно может принадлежать окну, которое еще не выпущено. Единственное, что вам действительно нужно беспокоиться - это сбалансировать собственное управление памятью. См. Apple's memory management guide. С точки зрения вашего 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
@bbum: какое объяснение неверно? Это стало очень длинным ответом, и я не уверен, в какой части вы ссылаетесь на ':)' –
@bbum: точка, отсчитанная по абсолютному счету сохранения, бесполезна. Я использовал их только для иллюстрации неявного сохранения в 'addSubview:'. –
Извините - количество задержек по инициализаторам * было * правильным (за исключением обсуждения абсолютного сохранения количества). И остальная часть вашего ответа - хотя довольно долго :) - неплохо. – bbum