2014-09-26 50 views
0

我们遇到了许多与核心数据相关的并发性相关崩溃以及与我们的应用程序一起删除,因此我创建了一个小型项目来重现其中一种情况。 在下列情况下,我能够重现“CoreData无法完成故障”的崩溃: - 我有2个子上下文A,B,它们都与相同的主父内容相关联。 - coredata模型非常简单,一个ConferenceRoom对象有许多Line对象。 - 上下文A和B具有并发类型“NSPrivateQueueConcurrencyType”,父类型为“NSMainQueueConcurrencyType” - 线程1从子上下文A中提取一个对象,对其进行错误处理,在上下文A和父上下文中将其删除 - 线程2获取来自子上下文B的相同对象等待2秒钟,将其保存在上下文B和主父上下文中。并发场景中的coredata删除崩溃:CoreData无法履行故障

- >在线程2应用程序崩溃,当它试图将其保存到孩子情境B

注意,我们已经尝试了新的xcode6 coredata调试标志后,已经修复的问题“CoreData不能履行故障” :“ - com.apple.CoreData.ConcurrencyDebug 1”,所以没有线程警告/理论上的问题... 所以任何人都可以解释我们如何避免这些崩溃? (如果需要,我可以发送完整的项目)。

下面是代码(我是新手iOS开发者,这是肯定的快速/肮脏的代码)

核心方法:

//this creates a Conference and a Line object (called from AppDelegate when app launches 
- (void) testCrash 
{ 


NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 
[formatter setDateFormat:@"dd-MM HH:mm:ss"]; 

NSString *initConfName = [formatter stringFromDate:[NSDate date]]; 

//fix 
[self.managedObjectChildContext performBlockAndWait:^{ 

    ConferenceRoom *room = [NSEntityDescription insertNewObjectForEntityForName:@"ConferenceRoom" inManagedObjectContext:self.managedObjectChildContext]; 

    room.name = initConfName; 

    Line *line1 = [NSEntityDescription insertNewObjectForEntityForName:@"Line" inManagedObjectContext:self.managedObjectChildContext]; 
    line1.phoneNumber = @"4154243243"; 

    NSMutableSet *lines = [room mutableSetValueForKey:@"lines"]; 
    [lines addObject:line1]; 
}]; 



[self saveChildContext]; 
[self saveContext]; 


NSThread* myThread = [[NSThread alloc] initWithTarget:self 
              selector:@selector(mainThread1:) 
              object:initConfName]; 
[myThread start]; 


NSThread* myThread2 = [[NSThread alloc] initWithTarget:self 
               selector:@selector(mainThread2:) 
               object:initConfName]; 
[myThread2 start]; 

} 



- (void) mainThread1:(NSString *) initConfName 
{ 
NSLog(@"started thread 1"); 

//GET OBJ FROM CHILD CONTEXT 1 



[self.managedObjectChildContext performBlockAndWait:^{ 

    NSArray *results = [self getConfRoom: self.managedObjectChildContext withName:initConfName]; 
    NSLog(@"C1 conf:%@", results); 

    ConferenceRoom *roomFoundChild1 = [results lastObject]; 

    NSArray *linesc1 = [[roomFoundChild1 mutableSetValueForKey:@"lines"] allObjects]; 
    Line *linec1 = [linesc1 firstObject]; 
    NSLog(@"LINEC1=%@", linec1); 

    //DELETE CONF IN CHILD CONTEXT 1: 


    NSLog(@"Thread1:going to delete conference %@", roomFoundChild1); 
    [self.managedObjectChildContext deleteObject: roomFoundChild1]; 
}]; 


NSLog(@"Thread1: before saving child context"); 
[self saveThisContext: self.managedObjectChildContext]; 
NSLog(@"Thread1: before saving main context"); 
//test: save in main context, works without this 
[self saveContext]; 



} 

- (void) mainThread2:(NSString*) initConfName 
{ 
NSLog(@"started thread 2"); 

//GET OBJ FROM CHILD CONTEXT 2 

__block NSArray *results; 
__block ConferenceRoom *roomFoundChild2; 
__block NSString *newName; 

[self.managedObjectChildTwoContext performBlockAndWait:^{ 
    results = [self getConfRoom: self.managedObjectChildTwoContext withName:initConfName]; 
    NSLog(@"C2 conf\n:%@", results); 

    roomFoundChild2 = [results lastObject]; 
    NSString *n = roomFoundChild2.name; 

    //UPDATE CONF ROOM IN CHILD CONTEXT 2 

    newName = [NSString stringWithFormat:@"%@-%@", initConfName, @"newName2"]; 

    NSLog(@"Thread 2 waiting"); 
    [NSThread sleepForTimeInterval:2]; 
    NSLog(@"Thread 2 resuming"); 

    roomFoundChild2.name = newName; 

    NSLog(@"roomFoundChild2, %@", roomFoundChild2); 

}]; 


NSLog(@"Thread2: before saving child context"); 
[self saveThisContext:self.managedObjectChildTwoContext]; 

NSLog(@"Thread2: after saving to child context"); 
results = [self getConfRoom:self.managedObjectChildTwoContext withName:newName]; 

NSLog(@"C2 context after delete:%@", results); 


NSLog(@"Thread2: before saving main context"); 
//test: save in main context, works without this 
[self saveContext]; 



} 




- (void)saveContext 
{ 
// NSError *error = nil; 
NSManagedObjectContext *managedObjectContext = self.managedObjectContext; 
if (managedObjectContext != nil) { 

    [managedObjectContext performBlockAndWait:^{ 

     if ([managedObjectContext hasChanges]) { 
      NSError *error = nil; 
      if (![managedObjectContext save:&error]) { 
      // Replace this implementation with code to handle the error appropriately. 
      // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
       NSLog(@"Unresolved ERROR %@, %@", error, [error userInfo]); 
       abort(); 

      }; 
     } 
    }]; 

} 

} 




- (void)saveChildContext 
{ 
// NSError *error = nil; 
NSManagedObjectContext *managedObjectContext = self.managedObjectChildContext; 
if (managedObjectContext != nil) { 

    //COREDATAFLAG CHANGE 


    [managedObjectContext performBlockAndWait:^{ 
     if ([managedObjectContext hasChanges]) { 

      NSError *error = nil; 
      if (![managedObjectContext save:&error]) { 
       // Replace this implementation with code to handle the error appropriately. 
       // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
       NSLog(@"Unresolved ERROR %@, %@", error, [error userInfo]); 
       abort(); 

      }; 
     } 
    }]; 


} 
} 



- (void) saveThisContext: (NSManagedObjectContext *)ctx 
{ 
if (ctx != nil) { 

    [ctx performBlockAndWait:^{ 

     if ([ctx hasChanges]) { 
      NSError *error = nil; 
      if (![ctx save:&error]) { 
        // Replace this implementation with code to handle the error appropriately. 
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
       NSLog(@"Unresolved ERROR %@, %@", error, [error userInfo]); 

        //removed abort to test.. 
        //abort(); 

      }; 
     } 
    }]; 

} 


} 
+0

当前试图复制你的错误,它将有助于有代码如何创建两个子上下文和你的getConfRoom:方法,虽然我'我只是以基本的方式创造他们,我认为他们应该完成。 – mitrenegade 2014-10-23 18:59:40

+0

我无法重现崩溃。如果你仍然在寻找答案,也许你可以为你的项目创建一个git仓库。 – mitrenegade 2014-10-23 19:29:34

回答

0

//test: save in main context, works without this [self saveContext];

你不能调用方法,因为它保存了主线程上下文,它只能从主线程保存/编辑/等。有几种处理方法,但你可以做的一件事是[self performSelectorOnMainThread:@selector(saveContext)];