У меня есть приложение с двумя отдельными TableViewControllers («TVC»), которые обращаются к тем же таблицам SQL CoreData. Пользователь вызывает TVC, нажимая кнопки в родительском ViewController. Однако записи, записанные в CoreData по вызовам подкласса NSManagedObject с помощью первого TVC, не были распознаны вторым TVC. Я решил, что создаю отдельные экземпляры моего UIManagedDocument, поэтому начал создавать экземпляр UIManagedDocument в родительском VC и передал документ в соответствующий TVC.Приложение iPhone падает при сохранении изменений в UIManagedObjectDocument
Теперь, когда я пытаюсь сохранить документ после записи записей, приложение падает, даже если я нахожусь в основном потоке, где был первоначально создан экземпляр документа.
Я иду об этом неправильно?
Вот родитель ViewController
//
#import "ITrackViewController.h"
#import "AthleteSearchTVController.h"
@interface ITrackViewController()
@end
@implementation ITrackViewController
@synthesize myFirstName = _myFirstName;
@synthesize myLastName = _myLastName;
@synthesize athleteDatabase = _athleteDatabase;
-(void) setAthleteDatabase:(UIManagedDocument *)athleteDatabase
{
if(_athleteDatabase != athleteDatabase)
{
_athleteDatabase = athleteDatabase;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
if (!self.athleteDatabase) { // for demo purposes, we'll create a default database if none is set
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:@"Default Athlete Database"];
NSLog(@"url for dataBase is %@.",url);
// url is now "<Documents Directory>/Default Athlete Database"
self.athleteDatabase = [[UIManagedDocument alloc] initWithFileURL:url]; // setter will create this for us on disk
}
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:@"BeginSearch"]) {
NSLog(@"firstName is %@ and lastName is %@.",firstName.text, lastName.text);
NSString *checkFirstName = [firstName.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSString *checkLastName = [firstName.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if ([checkFirstName length] < 1 || [checkLastName length] < 1) {
NSLog(@"Must include a value for both first and last names.");
} else {
[self dismissKeyboard:sender];
myFirstName = firstName.text;
myLastName = lastName.text;
UIManagedDocument *sharedAthleteDataBase = [self sharedDatabase];
NSString *searchName = [myFirstName stringByAppendingString:@"%20"];
searchName = [searchName stringByAppendingString:myLastName];
NSLog(@"searchName is %@ before segue.",searchName);
[segue.destinationViewController nameToSearchFor:searchName andUIManagedDoc:sharedAthleteDataBase];
}
}
}
-(IBAction)dismissKeyboard:(id)sender
{
[lastName endEditing:YES];
[firstName endEditing:YES];
}
- (UIManagedDocument *) sharedDatabase
{
__block UIManagedDocument *managedDocument = nil;
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:@"AthleteData"];
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.athleteDatabase.fileURL path]]) {
// does not exist on disk, so create it
[self.athleteDatabase saveToURL:self.athleteDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
if(success){
NSString* isMainThread;
if ([NSThread isMainThread]) {
isMainThread = @"on main thread.";
} else isMainThread = @"but not on main thread";
NSLog(@"athleteDatabase did not exist, now created %@", isMainThread);
managedDocument = self.athleteDatabase;
} else {
NSLog(@"Error creating athleteDatabase");
}
}];
} else if (self.athleteDatabase.documentState == UIDocumentStateClosed) {
// exists on disk, but we need to open it
[self.athleteDatabase openWithCompletionHandler:^(BOOL success) {
if(success){
NSString* isMainThread;
if ([NSThread isMainThread]) {
isMainThread = @"on main thread.";
} else isMainThread = @"but not on main thread";
NSLog(@"athleteDatabase was closed, is now open %@", isMainThread);
managedDocument = self.athleteDatabase;
} else NSLog(@"Error opening closed athleteDatabase");
}];
} else if (self.athleteDatabase.documentState == UIDocumentStateNormal) {
// already open and ready to use
managedDocument = self.athleteDatabase;
}
return managedDocument;
}
@end
Вот ТВЦ, что делает звонки на NSManagedObject Sub-класса первого извлечения данных из веб-API, а затем записать его на CoreData.
- (void)fetchAthleteSearchResultsIntoDocument:(UIManagedDocument *)document
whereNameIs:(NSString *)athleteName
{
dispatch_queue_t fetchQ = dispatch_queue_create("Athlete fetcher", NULL);
dispatch_async(fetchQ, ^{
NSString* isMainThread;
if ([NSThread isMainThread]) {
isMainThread = @"on main thread.";
} else isMainThread = @"but not on main thread";
NSLog(@"Preparing to get records %@", isMainThread);
NSArray *athleteRecords;
athleteRecords = [AthleticNetDataFetcher searchForMyAthleteWithName:athleteName];
[document.managedObjectContext performBlockAndWait:^{ // perform in the NSMOC's safe thread (main thread)
int iCount = 0;
for (NSDictionary *athleteInfo in athleteRecords) {
[ResultsForAthleteSearch resultsWithAthleteInfo:athleteInfo inManagedObjectContext:document.managedObjectContext
numberForSorting:iCount];
iCount = iCount + 1;
// table will automatically update due to NSFetchedResultsController's observing of the NSMOC
}
NSString* isMainThread;
if ([NSThread isMainThread]) {
isMainThread = @"on main thread.";
} else isMainThread = @"but not on main thread";
NSLog(@"Preparing to save results %@", isMainThread);
[document saveToURL:document.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
}];
});
}
Тренажер сбой при вызове [документ saveToURL: document.fileURL forSaveOperation: UIDocumentSaveForOverwriting completionHandler: NULL];
Журнал, который частично приведен ниже, говорит, что я на главной теме. Я не могу понять, почему это должно потерпеть крах. Это не сбой, если я прокомментирую вызов saveToURL и полагаюсь на автосохранение.
2013-10-02 13:46:22.459 iTrackTest[14989:c07] url for dataBase is file://localhost/Users/Phlipo/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/53E19DAE-6D2B-4B7F-A633-55C2BCA95AC5/Documents/Default%20Athlete%20Database/.
2013-10-02 13:46:31.031 iTrackTest[14989:c07] url for dataBase is file://localhost/Users/Phlipo/Library/Application%20Support/iPhone%20Simulator/6.1/Applications/53E19DAE-6D2B-4B7F-A633-55C2BCA95AC5/Documents/Default%20Athlete%20Database/.
2013-10-02 13:46:31.039 iTrackTest[14989:61f] Preparing to get records but not on main thread
2013-10-02 13:46:31.065 iTrackTest[14989:c07] athleteDatabase was closed, is now open on main thread.
2013-10-02 13:46:31.620 iTrackTest[14989:61f] [AthleticNetDataFetcher executeSearchRequest:] received {
.
[A bunch of data in JSON format].
.
}
2013-10-02 13:46:31.633 iTrackTest[14989:c07] Preparing to save results on main thread.
2013-10-02 13:47:23.258 iTrackTest[16150:3f07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.'
Я не сталкивался с этой ошибкой NSPErsistence при создании экземпляра MOD в TVC. Может быть, мне нужен более явный помощник DataBase?
Заранее благодарим за любую помощь.