【问题标题】:RestKit crashes because NSManagedObjectContext is nil in the RKResponseMapperOperationRestKit 崩溃,因为 NSManagedObjectContext 在 RKResponseMapperOperation 中为零
【发布时间】:2013-01-24 18:29:09
【问题描述】:

我正在处理我的文凭项目,其中包括一个带有 Core Data 数据库的 iOS 客户端和一个 Ruby on Rails 服务器。我正在使用 RestKit 进行它们之间的通信。目前我在让整个系统正常工作时遇到了一个大问题:当我尝试将响应映射到来自服务器的对象时,我得到以下异常

2013-02-08 22:40:43.947 App[66735:5903] *** Assertion failure in -[RKManagedObjectResponseMapperOperation performMappingWithObject:error:], ~/Repositories/App/RestKit/Code/Network/RKResponseMapperOperation.m:358
2013-02-08 23:04:30.562 App[66735:5903] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Unable to perform mapping: No `managedObjectContext` assigned. (Mapping response.URL = http://localhost:3000/contacts?auth_token=s78UFMq8mCQrr12GZcyx)'
*** First throw call stack:
(0x1de9012 0x1c0ee7e 0x1de8e78 0x16a4f35 0x8f56e 0x8d520 0x1647d23 0x1647a34 0x16d4301 0x23a253f 0x23b4014 0x23a52e8 0x23a5450 0x90ac6e12 0x90aaecca)
libc++abi.dylib: terminate called throwing an exception

我正在尝试从服务器加载联系人列表(数组),该列表应在 Core Data 中保存为“用户”。

我在 数据模型类 中构建了所有核心数据代码,就像我在这个视频中看到的那样:http://nsscreencast.com/episodes/11-core-data-basics。这里是:

头文件:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>

@interface AppDataModel : NSObject

+ (id)sharedDataModel;

@property (nonatomic, readonly) NSManagedObjectContext *mainContext;
@property (nonatomic, strong) NSManagedObjectModel *managedObjectModel;
@property (nonatomic, readonly) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (NSString *)modelName;
- (NSString *)pathToModel;
- (NSString *)storeFilename;
- (NSString *)pathToLocalStore;

@end

实现文件:

#import "AppDataModel.h"

@interface AppDataModel ()

- (NSString *)documentsDirectory;

@end

@implementation AppDataModel

@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize mainContext = _mainContext;

+ (id)sharedDataModel {
    static AppDataModel *__instance = nil;
    if (__instance == nil) {
        __instance = [[AppDataModel alloc] init];
    }

    return __instance;
}

- (NSString *)modelName {
    return @"AppModels";
}

- (NSString *)pathToModel {
    return [[NSBundle mainBundle] pathForResource:[self modelName]
                                           ofType:@"momd"];
}

- (NSString *)storeFilename {
    return [[self modelName] stringByAppendingPathExtension:@"sqlite"];
}

- (NSString *)pathToLocalStore {
    return [[self documentsDirectory] stringByAppendingPathComponent:[self storeFilename]];
}

- (NSString *)documentsDirectory {
    NSString *documentsDirectory = nil;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    documentsDirectory = [paths objectAtIndex:0];
    return documentsDirectory;
}

- (NSManagedObjectContext *)mainContext {
    if (_mainContext == nil) {
        _mainContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        _mainContext.persistentStoreCoordinator = [self persistentStoreCoordinator];
    }

    return _mainContext;
}

- (NSManagedObjectModel *)managedObjectModel {
    if (_managedObjectModel == nil) {
        NSURL *storeURL = [NSURL fileURLWithPath:[self pathToModel]];
        _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:storeURL];
    }

    return _managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (_persistentStoreCoordinator == nil) {
        NSLog(@"SQLITE STORE PATH: %@", [self pathToLocalStore]);
        NSURL *storeURL = [NSURL fileURLWithPath:[self pathToLocalStore]];
        NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc]
                                             initWithManagedObjectModel:[self managedObjectModel]];
        NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                                 [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                                 [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
        NSError *e = nil;
        if (![psc addPersistentStoreWithType:NSSQLiteStoreType
                               configuration:nil
                                         URL:storeURL
                                     options:options
                                       error:&e]) {
            NSDictionary *userInfo = [NSDictionary dictionaryWithObject:e forKey:NSUnderlyingErrorKey];
            NSString *reason = @"Could not create persistent store.";
            NSException *exc = [NSException exceptionWithName:NSInternalInconsistencyException
                                                       reason:reason
                                                     userInfo:userInfo];
            @throw exc;
        }

        _persistentStoreCoordinator = psc;
    }

    return _persistentStoreCoordinator;
}
@end

User 类非常简单,使用 xCode 自动生成。

头文件:

#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>


@interface User : NSManagedObject

@property (nonatomic, retain) NSString * email;
@property (nonatomic, retain) NSString * firstName;
@property (nonatomic, retain) NSString * lastName;
@property (nonatomic, retain) NSNumber * userID;

@end

实现文件:

#import "User.h"

@implementation User

@dynamic email;
@dynamic firstName;
@dynamic lastName;
@dynamic userID;

@end

就像数据模型类一样,我有一个用于通信的服务器管理器类

头文件:

#import <Foundation/Foundation.h>
#import <RestKit/RestKit.h>
#import "AppServerProtocol.h"
#import "AppDataModel.h"


@interface AppServer : NSObject <AppServerDelegate>

+ (id)sharedInstance;

@property (strong, nonatomic) RKObjectManager *objectManager;
@property (strong, nonatomic) RKEntityMapping *userMapping;

@end

以及实现文件:

#import "AppServer.h"
#import "User.h"
#import "Device.h"
#import "Ping.h"
#import "AppAppDelegate.h"

@interface AppServer ()

@property BOOL initialized;

@end

@implementation AppServer

+ (id)sharedInstance {
    static AppServer *__instance = nil;
    if (__instance == nil) {
        __instance = [[AppServer alloc] init];
        __instance.initialized = NO;
    }

    if (![__instance initialized]) {
        [__instance initServer];
    }

    return __instance;
}

- (void)initServer {
    // initialize RestKit
    NSURL *baseURL = [NSURL URLWithString:@"http://localhost:3000"];
    _objectManager = [RKObjectManager managerWithBaseURL:baseURL];

    // enable activity indicator spinner
    [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;

    // initialize managed object store
    _objectManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[[AppDataModel sharedDataModel] managedObjectModel]];

    _userMapping = [RKEntityMapping mappingForEntityForName:@"User" inManagedObjectStore:_objectManager.managedObjectStore];
    [_userMapping addAttributeMappingsFromDictionary:@{
     @"email" : @"email",
     @"firstName" : @"first_name",
     @"lastName" : @"last_name"
     }];
    [_userMapping setIdentificationAttributes: @[@"userID"]];

    RKResponseDescriptor *contactsResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:_userMapping pathPattern:@"/contacts" keyPath:nil statusCodes:nil];
    [_objectManager addResponseDescriptor:contactsResponseDescriptor];

    _initialized = YES;
}

// contacts
- (void)getContactsForCurrentUser {
    NSString *authToken = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppAuthenticationToken"];

    [_objectManager getObjectsAtPath:@"/contacts" parameters:@{@"auth_token": authToken} success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
        RKLogInfo(@"Load collection of contacts: %@", mappingResult.array);
    } failure:^(RKObjectRequestOperation *operation, NSError *error) {
        RKLogError(@"Operation failed with error: %@", error);
    }];

}

@end

因此,当我打开联系人表视图时,该视图已正确设置为使用获取的结果控制器(成功地将实体从数据库中拉出),我有一个危险的刷新按钮,它调用 方法 你刚刚读过上面的内容:

- (void)downloadContacts {
    [[AppServer sharedInstance] getContactsForCurrentUser];
}

这是响应的格式:

[
    {
        "created_at":"2013-01-11T14:03:57Z",
        "email":"john@example.com",
        "first_name":"John",
        "id":2,
        "last_name":"Doe",
        "updated_at":"2013-02-07T10:57:16Z"
    },
    {
        "created_at":"2013-01-11T14:03:57Z",
        "email":"jane@example.com",
        "first_name":"Jane",
        "id":3,
        "last_name":"Doe",
        "updated_at":"2013-02-07T10:57:16Z"
}
]

在异常之前控制台声明如下:

2013-02-08 22:40:36.892 App[66735:c07] I restkit:RKLog.m:34 RestKit logging initialized...
2013-02-08 22:40:36.994 App[66735:c07] SQLITE STORE PATH: ~/Library/Application Support/iPhone Simulator/6.0/Applications/D735548F-DF42-4E13-A7EF-53DF0C5D8F3B/Documents/AppModels.sqlite
2013-02-08 22:40:37.001 App[66735:c07] Context is ready!
2013-02-08 22:40:43.920 App[66735:c07] I restkit.network:RKHTTPRequestOperation.m:154 GET 'http://localhost:3000/contacts?auth_token=s78UFMq8mCQrr12GZcyx'
2013-02-08 22:40:43.945 App[66735:c07] I restkit.network:RKHTTPRequestOperation.m:181

RestKit 库的行,在抛出整个异常之前失败是:

NSAssert(self.managedObjectContext, @"Unable to perform mapping: No `managedObjectContext` assigned. (Mapping response.URL = %@)", self.response.URL);

我已经按照这个回到了 AppServer.m 文件中的 initServer 方法,在该方法返回之前,RKObjectManager 类的属性是这样的:http://imgur.com/LM5ZU9m

正如我调试的那样,我发现问题不在于服务器端或应用程序的通信 - 我可以看到接收到的 JSON 并反序列化为一个数组,但是当它传递给下一个方法时应该将它保存到 Core Data 中,由于托管对象上下文的 NSAssert,整个应用程序都会运行。

非常感谢任何帮助!

【问题讨论】:

    标签: ios ruby-on-rails core-data restkit nsmanagedobjectcontext


    【解决方案1】:

    经过几天的调试,我终于发现了问题所在:看来我还必须设置本地持久性存储的路径,并自己为托管对象存储生成托管对象上下文。

    我在这里找到了解决方案:https://github.com/RestKit/RestKit/issues/1221#issuecomment-13327693

    我刚刚在我的服务器初始化方法中添加了几行代码:

    NSError *error = nil;
    NSString *pathToPSC = [[AppDataModel sharedDataModel] pathToLocalStore];
    
    _objectManager.managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:[[AppDataModel sharedDataModel] managedObjectModel]];
    [_objectManager.managedObjectStore addSQLitePersistentStoreAtPath:pathToPSC fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
    
    if (error != nil) {
        NSLog(@"\nSerious object store error!\n");
        return;
    } else {
        [_objectManager.managedObjectStore createManagedObjectContexts];
    }
    

    【讨论】:

      【解决方案2】:

      我设法使用此函数RKApplicationDataDirectory() 来获取应用程序目录并设置我的数据库路径。

      // Initialize HTTPClient
      NSURL *baseURL = [NSURL URLWithString:@"http://myapiaddress.com"];
      AFHTTPClient* client = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
      //we want to work with JSON-Data
      [client setDefaultHeader:@"Accept" value:RKMIMETypeJSON];
      
      // Initialize RestKit
      RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
      NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"NameOfMyCoreDataModel" ofType:@"momd"]];
      
      //Iniitalize CoreData with RestKit
      NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
      RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
      NSError *error = nil;
      
      NSString *path = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"nameOfDB.sqlite"];
      
      objectManager.managedObjectStore = managedObjectStore;
      
      [objectManager.managedObjectStore addSQLitePersistentStoreAtPath:path fromSeedDatabaseAtPath:nil withConfiguration:nil options:nil error:&error];
      
      [objectManager.managedObjectStore createManagedObjectContexts];
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-27
        • 1970-01-01
        • 1970-01-01
        • 2015-10-07
        • 1970-01-01
        相关资源
        最近更新 更多