2014-09-26 51 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)];