BWDB SQLLite包装器发布错误

时间:2013-07-30 20:02:02

标签: objective-c sqlite

我在我的应用程序中使用最新ARC版本的Bill Weinman的BWDB,它的工作就好了。然而在Release中它崩溃了。而且只有在真实设备上,在模拟器中才能正常工作。我已经尝试过来自Lynda.com的最新BWDB文件,它也崩溃了。

我发现当你枚举结果时,在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变量中。

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

3 个答案:

答案 0 :(得分: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"]];
}

答案 1 :(得分:0)

  

__unsafe_unretained和__weak都会阻止对象的保留,但方式略有不同。对于__weak,指向对象的指针将在它指向的对象的释放时转换为nil,这是非常安全的行为。顾名思义,__ innafe_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编译器上,只有最优化ON。如果你有兴趣在他修理时听到,请跟随他的facebook page

与此同时,我通过将 enumRows 一个__strong所有权修改为我的代码,简单定义为:

NSDictionary * enumRows;

我改变了枚举函数只是为了设置一个__unsafe_unretained指针指向这个__strong 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;
    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;
  }
}

答案 2 :(得分: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个错误(在编译之前):

不兼容的指针类型初始化'__unsafe_unretained',表达式为'NSDictionary * __strong [1]

ARC

不允许隐式转换指向Objective-C指针的间接指针