Какой идиоматический способ управления количеством ссылок во время инициализаторов Obj-C? - PullRequest
3 голосов
/ 12 мая 2011

Я изучаю Objective-C.В моем первом нетривиальном проекте я столкнулся с вопросом о том, как лучше всего обрабатывать ресурсы, передаваемые инициализатору, по сравнению с инициализатором по умолчанию.У моего класса есть сохраненный ресурс engine, который можно установить вручную после создания, или во время инициализации явно, или во время инициализации по умолчанию:

- (id)init {
    if ((self = [super init])) {
        id e = [[XorShiftEngine alloc] init];
        [self setEngine: e];
        [e release];
    }
    return self;
}

- (id)initWithEngine:(NSObject <RandEngine> *)e {
    if ((self = [super init]))
        [self setEngine: e];
    return self;
}

- (id)setEngine:(NSObject <RandEngine> *)newEngine {
    [newEngine retain];
    [engine release];
    engine = newEngine;
    // Some other stuff which needs to happen on changing the engine.
    return engine;
}

Инициализатор по умолчанию, в частности, кажется очень уродливымя, чередуя код, относящийся к самому себе, затем к члену, затем снова к себе, затем снова к члену и присваивая имя объекту только для возможности его освобождения позже.Это также нарушает обозначенную идиому инициализатора.

Есть ли более идиоматический способ сделать это без использования пулов авто-выпуска?

Ответы [ 3 ]

3 голосов
/ 12 мая 2011

Вы говорите о e в -init?Я не вижу проблемы там ... вы создаете объект, вы используете его, вы выпускаете его.Это обычный и правильный шаблон для создания и использования объектов.Если вы недовольны этим, вы можете вместо этого -init передать nil в -initWithEngine: и -initWithEngine: создать движок, если ничего не предоставлено.«инициализатор по умолчанию» так же, как «назначенный инициализатор».Назначенный инициализатор - это тот, который вызывают все остальные инициализаторы - часто тот, который учитывает большинство настроек.В этом случае -initWithEngine: будет вашим назначенным инициализатором.

3 голосов
/ 12 мая 2011
- init
{
    self = [super init];
    if (self != nil) {
        // initialization here
    }
    return self;
}

Если у вас есть «инициализаторы покрытия», они должны следовать тому же шаблону:

- initWithBob:(Bob*)aBob
{
   self = [self init];
   if ( self != nil ) {
       ... deal with aBob here ...
   }
   return self;
}

Это также хороший пример того, почему разумно избегать нескольких инициализаторов в классе.Отслеживание может быть затруднительным и делает подклассы более утомительными и подверженными ошибкам.Лучше иметь один инициализатор и несколько подклассов или иметь один инициализатор и позволить конфигурировать экземпляры после факта.

Т.е. вместо initWithBob:, иметь @property(retain) Bob* bob;, который можно вызывать после init какнеобходимо.Хорошим правилом является ограничение инициализаторов до требуемого состояния.

Предполагая, что вы объявляете класс Car, я бы сделал что-то вроде:

@interface Car:NSObject
+ car;
+ carWithEngine:(Engine*)anEngine;
- initWithEngine:(Engine*)anEngine;
@end

Реализации должны быть очевидны;car создает механизм по умолчанию и вызывает alloc/initWithEngine:/autorelease.

Это дает вам один инициализатор, что делает простое подклассирование очевидным.

2 голосов
/ 12 мая 2011
- (id)init {
    id e = [[XorShiftEngine alloc] init];
    self = [self initWithEngine: e];
    [e release];
    return self;
}

// designated initializer
- (id)initWithEngine:(NSObject <RandEngine> *)e {
    self = [super init];
    if (self != nil) {
        engine = [e retain];
        // engine initialization stuff
    }
    return self;
}

- (id)setEngine:(NSObject <RandEngine> *)newEngine {
    [newEngine retain];
    [engine release];
    engine = newEngine;
    // Some other stuff which needs to happen on changing the engine.
    return engine;
}

Вы поступили в основном правильно.Единственное, что я хотел бы изменить, - это непосредственно назначить переменную и сохранить ее, а не вызывать установщик в методе init.

Apple рекомендует не вызывать установщики / получатели свойств в методах init и dealloc (я предполагаю, что выобъявили свойство retain и просто переопределяем установщик).

Если у вас есть код настройки движка, который должен произойти при первой установке движка в инициализаторе, а также при каждом его изменении, тогда выследует перестроить это в отдельный метод.

...