Блоки Objective C: существует ли способ избежать 'сам' быть сохраненным?

Я пытаюсь записать это максимально кратко, но не легко описать - поэтому благодарит читать =)

Я - основной разработчик Открытого исходного кода iPhone Framework Sparrow. Воробей смоделирован после библиотеки Flash AS3 и таким образом имеет систему событий точно так же, как AS3. В настоящее время та система работает путем определения селекторов - но я хотел бы развернуть ту систему путем разрешения использования блоков для слушателей события. Однако я спотыкаюсь проблемы управления памятью.

Я покажу Вам типичный пример использования событий - поскольку они обрабатываются теперь.

// init-method of a display object, inheriting from 
// the base event dispatcher class
- (id)init
{
    if (self = [super init])
    {
        // the method 'addEventListener...' is defined in the base class
        [self addEventListener:@selector(onAddedToStage:)
                      atObject:self
                       forType:SP_EVENT_TYPE_ADDED_TO_STAGE];
    }
    return self;
}

// the corresponding event listener
- (void)onAddedToStage:(SPEvent *)event
{
    [self startAnimations]; // call some method of self
}

Это довольно просто: Когда объект добавляется к дисплейному списку, он получает событие. В настоящее время базовый класс записывает слушателей события в массиве NSInvocation-объектов. NSInvocation создается способом, что он не сохраняет свою цель и аргументы. (Пользователь может заставить его сделать это, но в 99% случаев, это не необходимо).

То, что эти объекты не сохраняются, было сознательным выбором: иначе код выше вызвал бы стебель лука-порея памяти, даже если бы пользователь удалил слушателя события в dealloc-методе! Вот то, почему:

- (id)init
{
    if (self = [super init])
    {
        // [self addEventListener: ...] would somehow cause:
        [self retain]; // (A)
    }
    return self;
}

// the corresponding event listener
- (void)dealloc
{
    // [self removeEventListener...] would cause:
    [self release]; // (B)
    [super dealloc];
}

На первом взгляде, который кажется прекрасным: сохранение в init-методе соединяется выпуском в dealloc методе. Однако это не работает, так как dealloc метод никогда не будут называть, потому что сохранить количество никогда не достигает нуля!

Как я сказал, 'addEventListener... '-метод делает по точно этой причине, не сохраняют что-либо в ее версии по умолчанию. Из-за пути события работают (они почти всегда диспетчеризируются 'сам' или дочерние объекты, которые сохраняются так или иначе), который не является проблемой.

Однако и теперь мы приезжаем в центральную часть вопроса: Я не могу сделать этого с блоками. Посмотрите на вариант блока обработки событий, поскольку я хотел бы, чтобы это имело:

- (id)init
{
    if (self = [super init])
    {
        [self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
        {
            [self startAnimations];
        }];
    }
    return self;
}

Это выглядит большим и было бы очень просто в использовании. Однако: когда вызовы пользователя метод на 'сам' или использование, которое членская переменная в блоке - который будет, ну, в общем, почти всегда - блок, автоматически сохранит 'сам', и объект, никогда не будут dealloc'ed.

Теперь, я знаю, что любой пользователь мог исправить это путем создания __ ссылка блока на сам, как это:

__block id blockSelf = self;
[self addEventListenerForType:ADDED_TO_STAGE block:^(SPEvent *event)
{
    [blockSelf startAnimations];
}];

Но, честно, я уверен, что почти все пользователи не знали бы, чтобы сделать так или забыть делать так. API должен быть не только простым в использовании, но также и трудно неправильно использовать, и это ясно нарушает тот принцип. Пользователи API совершенно определенно неправильно использовали бы его.

То, что прослушивает меня, - то, что я знаю, что 'сам' не должен быть сохранен - это работает в моей текущей реализации, не сохраняя его. Таким образом, я хочу сказать блоку, что он не должен сохранять сам - меня, библиотеку, должен сказать блок это, так, чтобы пользователь не думал об этом.

В моем исследовании я не нашел способ сделать так. И я не могу думать о способе изменить мою архитектуру для установки тому ограничению блоков.

Кто-либо понял, что я мог делать с этим?
Даже если Вы не имеете, благодарит читать настолько далеко - я знаю, что это был подробный вопрос ;-)

5
задан PrimaryFeather 20 October 2010 в 07:39
поделиться