#iphone #animation #uiview #enumeration #objective-c-blocks
#iPhone #Анимация #uiview #перечисление #objective-c-блоки
Вопрос:
У меня есть набор анимаций, которые должны выполняться последовательно с различными проверками, выполняемыми на каждом шаге. Поскольку размер набора определяется во время выполнения, я ожидал использовать рекурсивный вызов… но у меня возникли проблемы с тем, чтобы заставить его функционировать в парадигме «блока».
Результатом является 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
}
}
Любая помощь приветствуется.
Спасибо!
Ответ №1:
Ответ на поставленный вопрос таков: да, рекурсивные вызовы при завершении блока допустимы.
@BillBrasky поднял хороший вопрос о том, что блок теряет область видимости. Я не знаю достаточно, чтобы сказать, требуется ли это или нет, поскольку я не обнаружил, что это проблема в моей ситуации. Кажется, у меня все работает правильно на каждой последующей итерации с помощью моей рекурсивной функции.
Основная проблема с кодом в том виде, в каком я его изначально написал и отправил, заключается в использовании FastEnumerator. Это ОПРЕДЕЛЕННО теряется, когда вы покидаете текущую функцию и переходите к другому циклу событий / новому разделу фрейма стека. Я понял, когда больше думал об этом, что, вероятно, за кулисами происходит довольно много, чтобы заставить FastEnumeration работать, и вполне логично, что выход из метода разрушит настройку.
В качестве исправления я заменил NSEnumerator простым целым числом, которое я затем увеличиваю каждый раз с помощью рекурсивной функции. Я не большой поклонник этого, поскольку это может привести к проблемам со стилем, выходящим за рамки, где, поскольку 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 amp;amp; index < [myArray count]) {
[self nextStep];
}
else {
// We are done, clean up
...
}
}];
}
Еще раз спасибо @BillBrasky, вы помогли указать мне правильный путь для решения этой проблемы. Я был слишком сосредоточен на рекурсии, и мой быстрый анализ моего объекта «self» выглядел нормально, потому что все, кроме одного элемента, было в порядке. Соедините это со сбоем отладчика в блоке, а не с фактической нарушающей строкой, и кто знает, как долго я бы смотрел на код, не видя реальной проблемы.
Приветствия.
Ответ №2:
Я тоже новичок в блоках, но я только что закончил с аналогичной проблемой. В моем случае EXEC_BAD_ACCESS был вызван тем, что блок вышел из области видимости. Я подозреваю, что когда-нибудь во 2-й рекурсии блок создается с нечетным фреймом стека, потому что он выполняется внутри другого блока.
Мое решение состояло в том, чтобы сохранить блоки в свойстве, помеченном как копия, например
@property (nonatomic,copy) BOOL (^callback)(DownloadProgress*);
Это гарантирует, что все сохранится в копии на случай, если исходный объект блока выйдет за пределы области видимости и будет скопирован.
Комментарии:
1. Я думаю, что ваш анализ ситуации правильный, плохой кадр стека, но сохранение блока в качестве свойства не решило это для меня. Однако это натолкнуло меня на некоторые идеи, и я начал копать немного глубже. Как я упоминал ранее, переменная ‘self’, по-видимому, действительна внутри блока (все значения, доступные для чтения пользователем, отображаются сплошными). Однако, похоже, что доступ к NSEnumerator вызывает сбой. Основываясь на вашем отзыве, я попытался обернуть это в свойство, но это также не решило проблему. Теперь мне интересно, полностью ли я неправильно понимаю проблему. Еще раз спасибо.
2. Дальнейшая отладка показывает, что это правильно, объект NSEnumerator вызывает проблему. Все остальные «собственные» ивары верны, но при первом вызове блока (после завершения первой анимации) stepEnumerator является поддельным, несмотря на то, что он помещен в свойство.