簡體   English   中英

在並發方案中刪除Coredata時崩潰:CoreData無法實現故障

[英]crash on coredata delete in concurrency scenario: CoreData could not fulfill a fault

我們的應用程序遇到了許多與核心數據和刪除相關的並發崩潰,因此我創建了一個小項目來重現其中一種情況。 在以下情況下,我可以使用“ CoreData無法完成故障”重現崩潰:-我有2個子上下文A,B,它們都與相同的主要父內容相關聯。 -coredata模型非常簡單,一個ConferenceRoom對象具有許多Line對象。 -上下文A和B的並發類型為“ NSPrivateQueueConcurrencyType”,父類的類型為“ NSMainQueueConcurrencyType”-線程1從子上下文A獲取一個對象,對其進行故障處理,在上下文A和主父上下文中將其刪除-線程2從其中獲取同一對象子上下文B,等待2秒鍾,將其保存在上下文B和主父上下文中。

->嘗試保存到子上下文B時,應用程序在線程2中崩潰,並顯示“ CoreData無法完成故障”

請注意,在嘗試了新的xcode6 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();

            };
        }
    }];

}


}

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

您無法從線程2調用該方法,因為它正在保存您的主線程上下文,該上下文只能從主線程保存/編輯/等。 有幾種處理方法,但是您可以做的一件事是[self performSelectorOnMainThread:@selector(saveContext)];

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM