RestKit Core Data NSError dealloc Crash

為{幸葍}努か 提交于 2019-12-19 03:59:13

问题


Trying to get to the bottom of an issue I've been seeing in production builds and FINALLY was able to reproduce it while testing. Using RestKit v0.23.1, when doing an RKManagedObjectRequestOperation using the following code (while plugged into instruments) I get "An Objective-C message was sent to a deallocated 'NSError' object (zombie)" and the app crashes every time there's objects in the response JSON - if the response is something like "objects = ();" there's no crash - so I'm guessing it's somewhere in the RestKit/Core Data mapping or storage?

    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];

The raw JSON is properly logged inside the setWillMapDeserializedResponseBlock, but the logs inside the success and error block are never reached. Here is the stack trace I get back from 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

回答1:


This isn't a problem with RestKit. I've seen this problem frequently and it actually looks like the over-release actually happens in Apple's code. The problem happens when you try to save to a Core Data store and it fails. Core Data reports an error as it should, but that error is mishandled.

I had a few scenarios causing the save failures and this is how I fixed them:

The data store is inaccessible because of the Data Protection API.

Either busy wait and let your app fail to launch like this:

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

Or disable protection if the data in your store isn't sensitive like this:

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

The important thing is that you don't try to save until you can access the file. If you can re-structure you code to prevent accessing the database when it is inaccessible that is good too. You can use the Data Protection API application delegate methods to trigger that mechanism.

The data store is corrupted - The best thing to do here is to delete the store and start over. Here is a good way to detect a corrupted store using the sqlite library directly.

    #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);

This runs a sqlite PRAGMA query to perform an integrity check. I use quick_check, but you could also use integrity_check if you are willing to wait the extra time. You can tell things are good using [result isEqualToString:@"ok"]



来源:https://stackoverflow.com/questions/23574607/restkit-core-data-nserror-dealloc-crash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!