2013-02-08 13 views
7

Tôi đang làm việc trên dự án bằng tốt nghiệp, bao gồm một khách hàng iOS với cơ sở dữ liệu Core Data và máy chủ Ruby on Rails. Tôi đang sử dụng RestKit để giao tiếp giữa chúng. Hiện nay tôi đang gặp một vấn đề lớn nhận được toàn bộ hệ thống để làm việc: khi tôi cố gắng để ánh xạ một phản ứng với các đối tượng từ các máy chủ, tôi nhận được ngoại lệ sau:RestKit bị treo vì NSManagedObjectContext là không trong RKResponseMapperOperation

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 

Tôi đang cố gắng để tải một danh sách (một mảng) các địa chỉ liên hệ từ máy chủ, sẽ được lưu dưới dạng "Người dùng" trong Dữ liệu chính.

Tôi đã cấu trúc tất cả mã Dữ liệu cốt lõi của mình trong một lớp Lớp mô hình dữ liệu, như tôi đã thấy trong video này: http://nsscreencast.com/episodes/11-core-data-basics. Ở đây là:

header file:

#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 

tập tin thực hiện:

#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 

Các tài lớp là khá đơn giản, tự động tạo ra với Xcode.

tập tin tiêu đề: tập tin

#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 

Thực hiện:

#import "User.h" 

@implementation User 

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

@end 

Cũng giống như các lớp mô hình dữ liệu, tôi có một máy chủ quản lý lớp mà tôi sử dụng để liên lạc:

Tiêu đề file:

#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 

Và thực hiện file:

#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 

Vì vậy, khi Tôi mở Danh bạ Bảng View, mà được thiết lập một cách chính xác để sử dụng một kết quả lấy điều khiển (thành công kéo các đối tượng ra khỏi DB), tôi có một làm mới nguy hiểm nút, trong đó kêu gọi phương pháp bạn vừa đọc ở trên:

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

Dưới đây là định dạng của phản ứng:

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

Và trước khi trừ console tiểu bang sau đây:

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 

Dòng của thư viện RestKit, thất bại trước khi toàn bộ ngoại lệ được ném là:

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

Tôi đã theo đó trở lại phương pháp initServer trong AppServer.m tập tin, trong đó, trước khi trở về phương pháp, các thuộc tính của lớp RKObjectManager là như thế này: http://imgur.com/LM5ZU9m

Như tôi đã sửa lỗi, tôi đã truy rằng vấn đề không phải là với phía máy chủ hoặc các thông tin liên lạc của ứng dụng - tôi có thể thấy JSON đã nhận và deserialized thành một mảng, nhưng thời điểm nó được chuyển đến phương thức tiếp theo được cho là lưu nó vào Core Data, toàn bộ ứng dụng đi kaboom vì NSAssert của bối cảnh đối tượng được quản lý.

Bất kỳ trợ giúp nào được đánh giá cao!

Trả lời

4

Sau vài ngày gỡ lỗi, cuối cùng tôi đã phát hiện ra điều gì đã xảy ra: có vẻ như tôi cũng phải đặt đường dẫn đến lưu trữ lưu trữ cục bộ của mình và tạo bối cảnh đối tượng được quản lý cho chính đối tượng được quản lý lưu trữ.

Đây là nơi tôi tìm thấy giải pháp: https://github.com/RestKit/RestKit/issues/1221#issuecomment-13327693

Tôi vừa mới bổ sung thêm một vài dòng trong phương pháp init máy chủ của tôi:

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

tôi cố gắng làm điều đó sử dụng chức năng này RKApplicationDataDirectory() để có được những ứng dụng và đặt đường dẫn cơ sở dữ liệu của tôi.

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