【问题标题】:RestKit Core Data NSError dealloc CrashRestKit 核心数据 NSError dealloc 崩溃
【发布时间】:2014-06-27 18:48:24
【问题描述】:

试图找出我在生产版本中看到的问题的根源,并且最终能够在测试时重现它。使用 RestKit v0.23.1,当使用以下代码执行 RKManagedObjectRequestOperation 时(插入仪器时)我得到“一个 Objective-C 消息被发送到一个已释放的 'NSError' 对象(僵尸)”并且应用程序在每次有对象时崩溃响应 JSON - 如果响应类似于“objects = ();”没有崩溃 - 所以我猜它在 RestKit/Core Data 映射或存储中的某个地方?

    RKManagedObjectRequestOperation *objectRequestOperation = [_objectManager managedObjectRequestOperationWithRequest:request managedObjectContext:_objectManager.managedObjectStore.mainQueueManagedObjectContext success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        DDLogInfo(@"INSIDE SUCCESS BLOCK");
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
        DDLogInfo(@"INSIDE ERROR BLOCK");
    }];

    [objectRequestOperation setWillMapDeserializedResponseBlock:^id(id deserializedResponseBody) {
        DDLogInfo(@"Response JSON: %@", deserializedResponseBody);

        return deserializedResponseBody;
    }];

    objectRequestOperation.savesToPersistentStore = YES;
    [objectRequestOperation start];

原始 JSON 已正确记录在 setWillMapDeserializedResponseBlock 中,但从未到达成功和错误块中的日志。这是我从 crashlytics 得到的堆栈跟踪:

Thread : Crashed: NSOperationQueue Serial Queue
0  libobjc.A.dylib                0x37dd4626 objc_msgSend + 5
1  Foundation                     0x2df5802d -[NSError dealloc] + 60
2  libobjc.A.dylib                0x37dd9b6b objc_object::sidetable_release(bool) + 174
3  libobjc.A.dylib                0x37dda0d3 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 358
4  CoreFoundation                 0x2d569501 _CFAutoreleasePoolPop + 16
5  Foundation                     0x2df69999 -[__NSOperationInternal _start:] + 1064
6  Foundation                     0x2e00d745 __NSOQSchedule_f + 60
7  libdispatch.dylib              0x382b8cbd _dispatch_queue_drain + 488
8  libdispatch.dylib              0x382b5c6f _dispatch_queue_invoke + 42
9  libdispatch.dylib              0x382b95f1 _dispatch_root_queue_drain + 76
10 libdispatch.dylib              0x382b98dd _dispatch_worker_thread2 + 56
11 libsystem_pthread.dylib        0x383e4c17 _pthread_wqthread + 298

【问题讨论】:

  • 所以你是在后台线程上运行它,但使用主线程上下文?
  • 这只会从主线程调用。
  • 是否有一些基于堆栈跟踪的东西或者让你认为它在后台线程中的东西,或者是基于我们上周在前一个线程中的对话?
  • 对不起,我不记得之前的对话了,但是堆栈跟踪从大量调度队列和工作线程的东西开始。如果您在主线程上显式启动操作,那么它应该在主线程上运行,尽管它可能会创建在(私有)操作队列上运行的其他操作(用于映射)。您无法从 Xcode 复制运行的糟糕透顶。您能否显示导致崩溃的映射和示例 JSON。
  • 这不在主线程上运行。您的问题是 NSError 正在被自动释放,然后发送了一条消息。如果不看 RestKit 的代码,它可能与那个“故障块”的内存管理有关。设计的保留周期很糟糕。

标签: ios core-data restkit dealloc nserror


【解决方案1】:

这不是 RestKit 的问题。我经常看到这个问题,实际上看起来过度发布实际上发生在 Apple 的代码中。当您尝试保存到 Core Data 存储但失败时,就会出现问题。 Core Data 应该报告一个错误,但该错误处理不当。

我遇到了一些导致保存失败的情况,这就是我修复它们的方法:

数据存储由于数据保护 API 而无法访问。

要么忙等待,让您的应用无法像这样启动:

while(![[UIApplication sharedApplication] isProtectedDataAvailable]) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5f]];
   }

如果您商店中的数据不敏感,则禁用保护,如下所示:

[_coordinator addPersistentStoreWithType:NSSQLiteStoreType
                               configuration:nil
                                         URL:url
            options:@{NSPersistentStoreFileProtectionKey:NSFileProtectionNone}
                                       error:&error];

重要的是,在您可以访问文件之前不要尝试保存。如果您可以重新构建代码以防止在无法访问数据库时访问数据库,那也很好。您可以使用数据保护 API 应用程序委托方法来触发该机制。

数据存储已损坏 - 最好的办法是删除存储并重新开始。这是直接使用 sqlite 库检测损坏存储的好方法。

    #import <sqlite3.h>

    sqlite3 *dbConnection;
    if (sqlite3_open([[url absoluteString] UTF8String], &dbConnection) != SQLITE_OK) {
        NSLog(@"[SQLITE] Unable to open database!");
    }
    sqlite3_stmt *statement = nil;
    sqlite3_prepare_v2(dbConnection, "PRAGMA quick_check;", -1, &statement, NULL);
    NSString *result = nil;
    while (sqlite3_step(statement) == SQLITE_ROW) {
        for (int i=0; i<sqlite3_column_count(statement); i++) {
            int colType = sqlite3_column_type(statement, i);
            if (colType == SQLITE_TEXT) {
                const unsigned char *col = sqlite3_column_text(statement, i);
                result = [NSString stringWithFormat:@"%s", col];
            } else {
                NSLog(@"[SQLITE] UNKNOWN DATATYPE");
            }
        }
    }
    sqlite3_close(dbConnection);

这会运行一个 sqlite PRAGMA 查询来执行完整性检查。我使用 quick_check,但如果您愿意等待额外的时间,您也可以使用 integrity_check。您可以使用 [result isEqualToString:@"ok"]

来判断事情是否良好

【讨论】:

  • 非常有趣和有用。不幸的是,我无法直接检查这是否有效,因为我无法在本地复制它,但我认为第一步将尝试文件保护设置并查看崩溃报告是否消失...... “保护”的术语是指信息的安全性,即个人/机密信息,而不是一些代表数据的随机服务器对象?
  • "重要的是在您可以访问文件之前不要尝试保存。" - 这可能是关键,因为在我的成功块中,我 USED TO 获取映射的对象并在它们上设置一个新值,然后一次保存它们,这并不是真的最伟大的事情。我最终做的是将它添加到托管对象的 willSave 方法中,所以我没有手动保存任何对象。也许这(一旦我发布具有此更改的新版本)将解决问题。
  • 如果您想了解有关“保护”位的更多信息,这很好地总结了它。 erik.io/blog/2014/01/03/… 您也可以观看 2012 年的 WWDC 会议。要回答您的问题,这与数据是否在磁盘上加密有关。如果它被加密并且用户设置了密码,那么直到用户输入密码才能解密。
  • 仅供参考,在一个实例中,我通过删除保护解决了这个问题,因为数据不敏感。在另一个中,我将所有数据库保存在我提交给 NSOperationQueue 的操作中。然后,我根据我是否可以访问数据库来切换队列中的暂停。
  • 如果我的选项字典中没有任何保护配置,这是否意味着它当前设置为无?我在文档中查看了“默认”,但没有看到它是什么。我目前只使用 NSMigratePersistentStoresAutomaticallyOption:@(YES), NSInferMappingModelAutomaticallyOption:@(YES),
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-09
  • 2023-03-14
  • 2018-09-02
  • 2023-03-10
  • 1970-01-01
相关资源
最近更新 更多