Рекурсивный вызов в рамках завершения блока в animateWithDuration? - PullRequest
0 голосов
/ 06 мая 2011

У меня есть набор анимаций, которые должны работать последовательно с различными проверками, выполняемыми на каждом шаге. Поскольку размер набора определяется во время выполнения, я ожидал использовать рекурсивный вызов ... но у меня возникли проблемы с его функционированием в рамках парадигмы «блок».

Результатом является EXEC_BAD_ACCESS независимо от того, был ли я предварительно объявлен блоком, используя

__block void (^myBlock)(BOOL) = ^(BOOL finished){ if (finished) [self nextStep];};

или нет, как показано в следующем фрагменте кода. После отладки выясняется, что переменная self действительно допустима.

NSEnumerator* stepEnumerator;

-(void) mainRoutine {
    stepEnumerator = [myArray objectEnumerator];
    [self nextStep];
}

-(void) nextStep {
    id obj;
    if ((obj = [stepEnumerator nextObject])) {
        // Do my checking at each location
        ....

        // we have another spot to move to
        [UIView animateWithDuration:animationDuration
                         animations:^{self.frame = newFrame;}
                         completion:^(BOOL finished){ if (finished) [self nextStep];}];
        }
    }
    else {
        // we are done, finish house cleaning
    }
}

Любая помощь очень ценится.
Спасибо!

Ответы [ 2 ]

5 голосов
/ 07 мая 2011

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

Основная проблема с кодом, как я его первоначально написал и представил, заключается в использовании FastEnumerator.Это окончательно теряется, когда вы покидаете текущую функцию и выходите в другой цикл обработки событий / новый раздел стекового фрейма.Когда я больше думал об этом, я понял, что, вероятно, за кулисами происходит немало того, чтобы заставить FastEnumeration работать, и вполне логично, что выход из этого метода разрушит настройку.NSEnumerator с простым целым числом, которое я затем каждый раз увеличиваю с помощью рекурсивной функции.Я не большой поклонник этого, поскольку это может привести к проблемам в стиле Out of Bounds, когда FastEnumerator не будет и не будет for (obj in array), но я не знаю другого решения.Я думаю, что я опубликую это как отдельный вопрос ...

Исправленный код:

int index;

-(void) mainRoutine {
    index = 0;
    if (index < [myArray count]) {
        [self nextStep];
    }
}

-(void) nextStep {
    // obtain the object from the array
    id obj = [myArray objectAtIndex:index++];
    // do my checking on the object
    ...

    [UIView animationWithDuration:animationDuration
                       animations:^{self.frame = [view frame];}
                      completions:^(BOOL finished) {
                          if (finished && index < [myArray count]) {
                              [self nextStep];
                          }
                          else {
                              // We are done, clean up
                              ...
                          }
                      }];
}

Еще раз спасибо @BillBrasky, вы помогли мне указать правильный путь для решения этой проблемы.Я был слишком сосредоточен на рекурсии, и мой быстрый анализ моего объекта ' self ' выглядел хорошо, потому что все за исключением для одного элемента было в порядке.Соедините это с отладчиком, ломающим блок, а не с самой оскорбительной строкой, и который знает, как долго я бы смотрел на код, не видя реальной проблемы.

Приветствия.

2 голосов
/ 07 мая 2011

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

Мое решение состояло в том, чтобы блоки были сохранены в помеченной копии свойства, например,

@property (nonatomic,copy) BOOL (^callback)(DownloadProgress*);

Это гарантирует, что все будет сохранено в копии на случай, если исходный объект блока выйдет из области видимости и будет считан GC'd.

...