发送到释放实例的 NSFastEnumeration 消息
NSFastEnumeration message sent to deallocated instance
我正在尝试为 sqlite 查询实现 NSFastEnumeration 协议。
我运行喜欢:
发送到释放实例的消息
- (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];
无需维护强大的对象数组。这是正确的解决方案吗?
快速枚举协议依赖于它正在枚举的集合保留其包含的项目。调用者(编译器)确保在枚举期间保留集合本身。
countByEnumeratingWithState:
使用的数组包含 __unsafe_unretained
个引用。这是安全的,因为编译器会保留集合,集合会保留项目,因此数组中的引用将保持有效。
在语言级别,通过快速枚举 return 调用的对象引用不为调用者所有,如果需要则必须保留,这当然由 ARC 自动处理。这与 return 从任何其他集合(数组、字典等)编辑的项目的处理方式没有什么不同。
现在您的 "collection" 不同了,它不包含项目,而是根据需要从 SQL 查询中获取项目。这些项目不属于您的 "collection",因此当不再有对它们的任何强引用时,ARC 将释放这些项目。因此,您存储在快速枚举 C 数组中的 __unsafe_unretained
引用确实是不安全的 - ARC 会释放它们引用的内容。
解决方案是向您的 "collection" 添加(即实例变量)标准集合,例如 NSMutableArray
。在每次调用 countByEnumeratingWithState:
时,首先清空此集合,从而丢弃您持有的对先前查询结果的任何引用(如果调用代码未保留它们,它们也会被释放),然后用查询结果填充它对于此调用将是 return。
当你的 "collection" 本身最终被 ARC 释放时,它仍然保留的任何对查询结果的引用也将被丢弃。
值得一读Apple's Enumeration Sample,因为它的注释包含实现快速枚举所需的内存管理的详细信息。
HTH
我正在尝试为 sqlite 查询实现 NSFastEnumeration 协议。
我运行喜欢: 发送到释放实例的消息
- (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];
无需维护强大的对象数组。这是正确的解决方案吗?
快速枚举协议依赖于它正在枚举的集合保留其包含的项目。调用者(编译器)确保在枚举期间保留集合本身。
countByEnumeratingWithState:
使用的数组包含 __unsafe_unretained
个引用。这是安全的,因为编译器会保留集合,集合会保留项目,因此数组中的引用将保持有效。
在语言级别,通过快速枚举 return 调用的对象引用不为调用者所有,如果需要则必须保留,这当然由 ARC 自动处理。这与 return 从任何其他集合(数组、字典等)编辑的项目的处理方式没有什么不同。
现在您的 "collection" 不同了,它不包含项目,而是根据需要从 SQL 查询中获取项目。这些项目不属于您的 "collection",因此当不再有对它们的任何强引用时,ARC 将释放这些项目。因此,您存储在快速枚举 C 数组中的 __unsafe_unretained
引用确实是不安全的 - ARC 会释放它们引用的内容。
解决方案是向您的 "collection" 添加(即实例变量)标准集合,例如 NSMutableArray
。在每次调用 countByEnumeratingWithState:
时,首先清空此集合,从而丢弃您持有的对先前查询结果的任何引用(如果调用代码未保留它们,它们也会被释放),然后用查询结果填充它对于此调用将是 return。
当你的 "collection" 本身最终被 ARC 释放时,它仍然保留的任何对查询结果的引用也将被丢弃。
值得一读Apple's Enumeration Sample,因为它的注释包含实现快速枚举所需的内存管理的详细信息。
HTH