NSFastEnumeration сообщение отправлено на освобожденный экземпляр
Я пытаюсь реализовать протокол NSFastEnumeration для запроса sqlite.
Я сталкиваюсь с: сообщение отправлено на освобожденный экземпляр
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len {
// First call
if(state->state == 0) {
state->mutationsPtr = &state->extra[0];
state->state = 1;
sqlite3_reset(self.statement);
}
state->itemsPtr = stackbuf;
NSUInteger count = 0;
while (count < len) {
int result = sqlite3_step(self.statement);
if (result == SQLITE_DONE) {
break;
}
MyRow *row = [self queryRow];
stackbuf[count] = row;
count += 1;
}
return count;
}
-(MyRow *) queryRow {
MyRow * row = // query sqlite for row
return row;
}
Кажется, что объект 'row' не сохраняется, поэтому, когда к нему нужно обращаться в цикле, он уже освобожден.
Нужно ли сохранять результаты при итерации в countByEnumeratingWithState в сильном наборе данных, чтобы он сохранялся?
IE:
@property (nonatomic, strong) NSMutableArray *resultList;
Затем внутри цикла while:
while (count < len) {
int result = sqlite3_step(self.statement);
if (result == SQLITE_DONE) {
break;
}
MyRow *row = [self queryRow];
[self.resultList addObject:row]; // throw into a strong array so its retained
stackbuf[count] = row;
count += 1;
}
РЕДАКТИРОВАТЬ:
Немного больше исследований показывает, что, возможно, я могу просто использовать __autoreleasing:
MyRow * __autoreleasing row = [self queryRow];
Без необходимости поддерживать сильный массив объектов. Это правильное решение?
1 ответ
Протокол быстрого перечисления опирается на коллекцию, которую он перечисляет, сохраняя содержащиеся в нем элементы. Вызывающая сторона (компилятор) гарантирует, что сама коллекция сохраняется во время перечисления.
Массив, используемый countByEnumeratingWithState:
содержит __unsafe_unretained
Рекомендации. Это безопасно, так как компилятор сохраняет коллекцию, коллекция сохраняет элементы, и поэтому ссылки в массиве останутся действительными.
На уровне языка ссылка на объект, возвращаемая быстрым перечислением, не вызывается вызывающей стороной и должна быть сохранена в случае необходимости, что, конечно, автоматически обрабатывается ARC. Это ничем не отличается от того, как обрабатываются элементы, возвращенные из любой другой коллекции (массивы, словари и т. Д.).
Теперь ваша "коллекция" отличается, она не содержит элементов, но получает их по запросу SQL. Эти предметы не принадлежат вашей "коллекции" и поэтому освобождаются ARC, когда на них больше нет сильных ссылок. Следовательно __unsafe_unretained
ссылки, которые вы храните в быстрых перечислениях, массив C действительно опасен - ARC освобождает то, на что они ссылаются.
Решение состоит в том, чтобы добавить (т.е. переменную экземпляра) стандартную коллекцию, скажем, NSMutableArray
, к вашей "коллекции". На каждый звонок countByEnumeratingWithState:
сначала очистите эту коллекцию, отбросив, таким образом, любые ссылки, которые вы держите на результаты предыдущего запроса (что также освободит их, если вызывающий код не сохранил их), а затем заполните ее результатами запроса, которые будут возвращены для этого вызова.
Когда ARC окончательно освобождает вашу "коллекцию", любые ссылки на результаты запроса, которые она все еще хранит, также будут отброшены.
Стоит прочитать пример перечисления Apple, поскольку в его комментариях содержится подробная информация об управлении памятью, необходимом для реализации быстрого перечисления.
НТН