решение не так просто, как кажется на первый взгляд.Есть некоторые опасности при инициализации - подробнее об этом ниже.по этим причинам я обычно использую один из двух следующих подходов в программах objc:
для тривиальных случаев дублирование не является плохой стратегией:
- (id)initOne
{
self = [super init];
if (nil != self) { monIntIvar = SomeDefaultValue; }
return self;
}
- (id)initTwo
{
self = [super init];
if (nil != self) { monIntIvar = SomeDefaultValue; }
return self;
}
для нетривиальных случаев, я рекомендуюстатическая функция инициализации, которая принимает общий вид:
// MONView.h
@interface MONView : UIView
{
MONIvar * ivar;
}
@end
// MONView.m
static inline bool InitMONView(MONIvar** ivar) {
*ivar = [MONIvar new];
return nil != *ivar;
}
@implementation MONView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (nil != self) {
if (!InitMONView(&ivar)) {
[self release];
return nil;
}
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (nil != self) {
if (!InitMONView(&ivar)) {
[self release];
return nil;
}
}
return self;
}
// …
@end
Objective-C ++:
если вы используете objc ++, то вы можете просто реализовать подходящие конструкторы по умолчанию дляваши c ++ ivars и опускают большую часть инициализации и освобождают леса (при условии, что вы правильно включили флаги компилятора).
Обновление:
Я собираюсь объяснить, почемуэто безопасный способ инициализации объекта и определения некоторых причин, по которым типичные реализации других ответов опасны.
Типичная проблема с обычными инициализаторами, которые вызывают методы экземпляра во время инициализации, заключается в том, что они злоупотребляют графом наследования, как правило,ввести сложности и ошибки.
Рекомендация: вызовпереопределенные методы экземпляра на частично сконструированном объекте (например, во время инициализации и освобождения) небезопасны и их следует избегать.аксессоры особенно плохи.В других языках это ошибка программиста (например, UB).Просмотрите документацию objc на эту тему (ссылка: «Реализация инициализатора»).Я считаю это обязательным, но я все еще знаю людей, которые настаивают на том, что методы экземпляров и методы доступа лучше в частично сконструированных состояниях, потому что это " обычно работает для них".
Требование: Соблюдайте график наследования.инициализировать от базы вверх.уничтожить сверху вниз.всегда.
Рекомендация: сохранить инициализацию согласованной для всех.если ваша база возвращает что-то из init, вы должны предполагать, что все хорошо.не вводите хрупкий танец инициализации для ваших клиентов и подклассов для реализации (это может вернуться как ошибка).вам нужно знать, есть ли у вас действительный экземпляр.Кроме того, субклассеры (по праву) предполагают, что ваша база правильно инициализирована, когда вы возвращаете объект из назначенного инициализатора.Вы можете уменьшить вероятность этого, сделав ивары базового класса приватными.как только вы вернетесь из init, клиенты / подклассы предполагают, что объект, из которого они получены, может использоваться и правильно инициализироваться.По мере роста графов классов ситуация становится очень сложной, и ошибки начинают появляться.
Рекомендация: проверка ошибок в init.также сохраняйте обработку ошибок и обнаружение последовательными.возврат nil - очевидное соглашение, чтобы определить, была ли ошибка во время инициализации.обнаружить его рано.
Хорошо, а как насчет метода общего экземпляра?
exmaple заимствован и изменен из другого поста:
@implementation MONDragon
- (void)commonInit
{
ivar = [MONIvar new];
}
- (id)initWithFrame:(CGRect)aRect
{
if ((self = [super initWithFrame:aRect])) {
[self commonInit];
}
return self;
}
- (id)initWithCoder:(NSCoder*)coder
{
if ((self = [super initWithCoder:coder])) {
[self commonInit];
}
return self;
}
// …
(кстати, обработки ошибок вэтот пример)
Калеб: самая большая «опасность», которую я вижу в приведенном выше коде, заключается в том, что кто-то может создать подкласс рассматриваемого класса, переопределить -commonInit и потенциально инициализировать объект дважды.
в частности, подкласс - [MONDragon commonInit] будет вызываться дважды (утечка ресурсов, поскольку они будут созданы дважды) и инициализатор базы и обработка ошибок не будут выполняться.
Калеб: Если это реальный риск ...
любой эффект может приравнять к ненадежной программе.Эту проблему легко избежать, используя обычную инициализацию.
Caleb:… самый простой способ справиться с этим - сохранить -commonInit закрытым и / или задокументировать его как нечто, что не следует переопределять
, поскольку среда выполнения не различает видимость при обмене сообщениями, этот подход опасен, поскольку любой подкласс может легко объявить один и тот же метод приватной инициализации (см. Ниже).
документирование метода как чего-то, что вы не должны переопределять, обременяет подклассы и создает сложности и проблемы, которых можно легко избежать - используя другие подходы.это также подвержено ошибкам, так как компилятор не пометит его.
, если кто-то настаивает на использовании метода экземпляра, зарезервированное вами соглашение, такое как -[MONDragon constructMONDragon]
и -[MONKomodo constructMONKomodo]
, может значительно уменьшить ошибку в большинствеслучаев.инициализатор, вероятно, будет виден только для TU реализации класса, поэтому компилятор может пометить некоторые из наших потенциальных ошибок.
примечание: общий конструктор объекта, такой как:
- (void)commonInit
{
[super commonInit];
// init this instance here
}
(что я также видел) еще хуже, потому что оно ограничивает инициализацию, удаляет контекст (например, параметры), и вы по-прежнему получаете людей, смешивающих свой код инициализации между классами между назначенным инициализатором и -commonInit
.
*Из-за всего этого, потратив много времени на отладку всех вышеперечисленных проблем из-за общих недоразумений и глупых ошибок / недосмотров, я пришел к выводу, что статическую функцию легче всего понять и поддерживать, когда вам нужно реализовать общую инициализацию дляучебный класс.классы должны оградить своих клиентов от опасностей, проблема, при которой «общий инициализатор через метод экземпляра» неоднократно заканчивался неудачей.
это не опция в OP, основанная на указанном методе, а общее замечание: выможет обычно объединять общую инициализацию легче, используя удобные конструкторы.это особенно полезно для минимизации сложности при работе с кластерами классов, классами, которые могут возвращать специализации, и реализациями, которые могут выбрать выбор из нескольких внутренних инициализаторов.