0

我在我的应用程序中使用了最新的 ARC 版本的 Bill Weinman 的 BWDB,它在 Debug 中运行良好。但是在 Release 中它崩溃了。并且仅在真实设备上,在模拟器中它工作得很好。我尝试了 Lynda.com 上最新的 BWDB excersise 文件,但它也崩溃了。

我发现当您枚举结果时,指向行的指针已经在 forin 循环内释放

for (NSDictionary *firstSpecies in [sql getFirstSpeciesName]) 
{
  //firstSpecies is already released here
  m_speciesName = [firstSpecies objectForKey:@"FirstSpeciesName"];
}

这让我相信在实施中存在某种错误

(NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len 

或在 enumRows 变量中。

你知道如何解决这个问题吗?

4

3 回答 3

1

Bill Weinman 的 BWDB 已针对 iOS 7 进行了更新,这也可以解决问题。

RSSDB.m

iOS 7 SDK 中的一个错误阻止了它在 ARM 上的工作

// for (row in [self getQuery:@"SELECT id FROM feed ORDER BY LOWER(title)"]) {
//     [idList addObject:row[@"id"]];
// }

iOS 7 的解决方法

[self prepareQuery:@"SELECT id FROM feed ORDER BY LOWER(title)"];
while ((row = [self getPreparedRow])) {
    [idList addObject:row[@"id"]];
}
于 2013-12-27T14:11:32.190 回答
0

__unsafe_unretained 和 __weak 都阻止了对象的保留,但方式略有不同。对于 __weak,指向对象的指针将在释放它指向的对象时转换为 nil,这是非常安全的行为。顾名思义,__unsafe_unretained 将继续指向对象所在的内存,即使在对象被释放后也是如此。由于访问该解除分配的对象,这可能会导致崩溃。

在 BWDB 示例中有一行存储到枚举函数中的实例变量enumRows

- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len 
{
  if ((*enumRows = [self getPreparedRow]))
  {
     state->itemsPtr = enumRows;
     state->state = 0;   // not used, customarily set to zero
     state->mutationsPtr = state->extra;   // also not used, required by the interface
    return 1;
  } else {
     return 0;
  }
}

并且enumRows定义为:

__unsafe_unretained NSDictionary * enumRows[1];

方法 GetPreparedRow 声明如下:

- (NSDictionary *) getPreparedRow 
{
int retCode = sqlite3_step(m_statement);

if (retCode == SQLITE_DONE) 
{
    sqlite3_finalize(m_statement);
    return nil;
} 
else  if (retCode == SQLITE_ROW) 
{
    int col_count = sqlite3_column_count(m_statement);
    if (col_count >= 1) 
    {
        NSMutableDictionary * dRow = [NSMutableDictionary dictionaryWithCapacity:1];
        for(int i = 0; i < col_count; i++) 
        {
            NSString * columnName = [NSString stringWithUTF8String:sqlite3_column_name(m_statement, i)];
            [dRow setObject:[self columnValue:i] forKey:columnName];
        }
        return dRow;
    }
} 
else 
{
    NSLog(@"rowFromPreparedQuery: could not get row: %s", sqlite3_errmsg(m_database));
    return nil;
}
return nil;
}

所以基本上从 GetPreparedRow 方法返回的NSDictiornay * 存储到 __unsafe_unretained 实例变量enumRows中。随着GetPreparedRow完成,NSDictionary * 被释放,因为它超出了范围,但enumRows仍然指向这个内存。因此可以枚举行,但每个当前行都指向无效内存。

我不确定为什么这在 Debug 和 Release/Simulator 中有效,但可能是内存没有立即被擦除或覆盖,所以enumRows仍然指向有效内存。我写信给 Bill Weinman,他说只有最新版本的 LLVM 编译器才会发生这种情况,并且只有优化开启。如果您有兴趣听到他修复它,请关注他的Facebook 页面。

同时,我通过使enumRows成为 __strong 所有权在我的代码中修复了它,简单地定义为:

NSDictionary * enumRows;

我改变了枚举函数只是为了设置一个指向这个 __strong enumRows指针的 __unsafe_unretained 指针

- (NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len 
{
  if ((enumRows = [self getPreparedRow]))
  {
    __unsafe_unretained id row = enumRows;
    state->itemsPtr = &row;
    state->state = 0;   // not used, customarily set to zero
    state->mutationsPtr = state->extra;   // also not used, required by the interface
    return 1;
  } else {
    return 0;
  }
}
于 2013-07-31T06:49:41.253 回答
0

@DaNY

如果我定义

NSDictionary * enumRows;

接着

(NSUInteger) countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len 
{
  if ((enumRows = [self getPreparedRow]))
  {
    __unsafe_unretained id row = enumRows;
    state->itemsPtr = &row;
...

我收到 1 个警告和 1 个错误(编译前):

使用“NSDictionary * __strong[1]”类型的表达式初始化“__unsafe_unretained”的不兼容指针类型

ARC 不允许将间接指针隐式转换为 Objective-C 指针

于 2014-01-04T09:42:59.557 回答