Цель C - является ли init плохим местом для реализации фабрики?

Я реализовал старый шаблон init-as-a-factory, но в одном конкретном случае (но не в других!) Анализатор получает предупреждение об утечках памяти. И действительно, глядя на правила политики управления памятью какао, это allocне init, который может вернуть +1-retain-count объектов.

Так что получается, что:

  1. Высвобождение self и возвращая новый объект из init строго говоря, против правил.
  2. Многие места в Интернете продвигают эту технику, и из-за тандемной природы alloc/init это работает.
  3. Анализатор иногда жалуется на это, а иногда нет.

Так... мы все время делали это неправильно?

3 ответа

Вы можете реализовать init как этот, который должен выпустить self чтобы сбалансировать удерживать счет от alloc вызов.

- (id)initWithSomething:(id)something
{
    [self release]; // don't need this line for ARC
    self = nil;
    return [[PrivateSubClass alloc] initWithSomething:something];
}

и это если очень часто реализовать init как заводской метод. например NSArray, NSDictionary, NSString

Как сказал Гейдж, будет гораздо понятнее, если вы разместите фрагмент кода, а не объяснения.

В любом случае, вы можете переместить свою фабрику в метод класса, так что у вас не будет такой проблемы вообще. Я имею в виду что-то вроде этого:

MyClass* instance = [MyClass instanceWithParameters:params];

@interface MyClass
+ (MyClass*) instanceWithParameters:(ParamType)params;
@end

Трудно сказать, не зная, что за код вызывает поведение анализатора, но, как правило, вот пара удобных для компилятора способов определения методов init/factory.

Классический alloc/init

- (instancetype)initWithParameter:(id)parameter {
    if(self = [super init]) {
       _parameter = parameter; 
    }
    return self;
}

использование

MyCustomClass * myInstance = [[MyCustomClass alloc] initWithParameter:foo];

Это создаст экземпляр со счетом удержания +1. Под ARC это будет автоматически управляться должным образом, поскольку оно следует правилу NARC (New, Alloc, Retain, Copy). По той же причине в средах до ARC он должен быть явно освобожден клиентом.

Пользовательский метод фабрики

ARC

+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
    return [[self alloc] initWithParameter:parameter]; // assuming -initWithParameter: defined
}

Pre-ARC

+ (instancetype)canIHazInstanceWithParameter:(id)parameter {
    return [[[self alloc] initWithParameter:parameter] autorelease]; // assuming -initWithParameter: defined
}

использование

MyCustomClass * myInstance = [MyCustomClass canIHazInstanceWithParameter:foo];

Как в ARC, так и в pre-ARC метод возвращает автоматически выпущенный экземпляр (это явно более очевидно в реализации до ARC), который не должен управляться клиентом.

замечания

  • Вы могли заметить instancetype ключевое слово. Это удобное расширение языка, представленное Clang, которое превращает компилятор в дорогого друга при реализации ваших собственных методов конструкторов / фабрики. Я написал статью на эту тему, которая может иметь отношение к вам.

  • Являются ли фабричные методы предпочтительнее init методы спорны. С точки зрения клиента это не имеет большого значения в ARC, при условии, что вы тщательно соблюдаете соглашения об именах, хотя я лично склонен показывать фабричные методы в интерфейсе при реализации пользовательских init методы только внутри (как я делал в примерах выше). Это больше вопрос стиля, чем практическая забота.

Другие вопросы по тегам