【问题标题】:Saving coredata on background thread causes fetching into a deadlock and crash在后台线程上保存 coredata 会导致获取死锁和崩溃
【发布时间】:2015-05-21 08:55:24
【问题描述】:

我将 coredata 保存在后台方法(父子)中,但获取是在主线程上完成的。所以我在 fetch 方法中遇到了死锁,有时应用程序崩溃了。我做错了什么吗?如何在不影响主线程的情况下同时改进保存和获取?我已经阅读了很多文档,但没有一篇解释我如何在项目中使用这两者。如果这是一个错误的问题,请指导我找到正确的解决方案并让我知道我的错误。

-(ThreadInfo *)retrieveSolicitationInfoForThreadID:(NSString*)inThreadID;
{
NSString *loginUser=[[NSUserDefaults standardUserDefaults]    valueForKey:@"currentUser"];

AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ThreadInfo"
                                      inManagedObjectContext:context];
[fetchRequest setEntity:entity];
 NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"userEmail ==   %@",loginUser];
 NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:@"threadID == %@",inThreadID];

NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: @[userPredicate, threadPredicate]];

[fetchRequest setPredicate:compoundPredicate];

 NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
if(fetchedObjects.count!=0)
{
    ThreadInfo *threadInfo=[fetchedObjects objectAtIndex:0];
    return threadInfo;
}
 return nil;
}  

保存

-(void)updateThreadEntityWithSyncDetails:(NSMutableDictionary *)inDictionary
  {

 NSString *loginUser=[[NSUserDefaults standardUserDefaults] valueForKey:@"currentUser"];

 AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [sharedDelegate managedObjectContext];

  //    NSManagedObjectContext *writerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
  //   [writerContext setPersistentStoreCoordinator:[sharedDelegate persistentStoreCoordinator]];

 // create main thread MOC
   context = [[NSManagedObjectContext alloc]        initWithConcurrencyType:NSMainQueueConcurrencyType];
     context.parentContext = writerContext;

    NSManagedObjectContext *contextforThread = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

   contextforThread.parentContext = context;

  [contextforThread performBlock:^{




NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ThreadInfo"
                                          inManagedObjectContext:contextforThread];
[fetchRequest setEntity:entity];
NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"userEmail == %@",loginUser];
NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:@"threadID == %@",[inDictionary valueForKey:@"thread"]];
NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: @[userPredicate, threadPredicate]];
[fetchRequest setPredicate:compoundPredicate];

NSArray *fetchedObjects = [contextforThread executeFetchRequest:fetchRequest error:nil];
for (ThreadInfo *threadInfo in fetchedObjects)
{
    if([[inDictionary allKeys] containsObject:@"userEmail"])
    {
        if([inDictionary valueForKey:@"userEmail"]!=[NSNull null])
        {
            threadInfo.userEmail=[inDictionary valueForKey:@"userEmail"];
        }
    }
    if([[inDictionary allKeys] containsObject:@"badgeValue"])
    {
        if([inDictionary valueForKey:@"badgeValue"]!=[NSNull null])
        {
            threadInfo.badgeValue=[inDictionary valueForKey:@"badgeValue"];
        }
    }


    if([[inDictionary allKeys] containsObject:@"choice4Percentage"])
    {
        if([inDictionary valueForKey:@"choice4Percentage"]!=[NSNull null])
        {
            threadInfo.choice4Percentage=[inDictionary valueForKey:@"choice4Percentage"];
        }
    }
    if([[inDictionary allKeys] containsObject:@"choice5Percentage"])
    {
        if([inDictionary valueForKey:@"choice5Percentage"]!=[NSNull null])
        {
            threadInfo.choice5Percentage=[inDictionary valueForKey:@"choice5Percentage"];
        }
    }

 }

    NSError *error;
    if(![context save:&error]) {
        NSLog(@"Child error : %@",error);

    }

    [context performBlock:^{
        NSError *error;
        if(![context save:&error]) {
            NSLog(@"%@",error);
        }
               }];
    }];


 }    

【问题讨论】:

    标签: ios objective-c multithreading core-data


    【解决方案1】:

    问题在于您的获取。您正在获取两个字符串,我猜您的商店中有大量实体。您的抓取性能是您问题的 100%。

    A) 如果可以避免,您不应该进行字符串比较 B)你不应该做两次字符串比较

    您的数据模型需要重构。无论如何,对具有大量实体的表进行两个字符串比较都会表现得很糟糕。

    更新

    你的数据结构不好。我不知道我可以说多少种方式。理想情况下,您需要一个整数或其他数字结构作为您的唯一标识符。

    由于您没有告诉我/我们您正在处理多少实体,我只能猜测您的数量很大。预期结果如何?一?还是很多?这是您正在努力应对的独特约束吗?如果没有,为什么没有来自您的服务器的唯一约束?如果这是一个独特的约束,你为什么不像我建议的那样限制获取大小?

    你没有给我任何信息,但希望我能神奇地解决你的问题。

    您可以尝试将其减少为一个字符串,但您是在问您的数据将如何表现。测试它。你有仪器,你有数据。改变事物并比较结果。

    字符串很昂贵。他们一直都是,而且永远都是。

    更新

    由于它是一个独特的约束,您可以帮自己一个大忙并稍微更改您的代码:

    - (ThreadInfo *)retrieveSolicitationInfoForThreadID:(NSString*)inThreadID;
    {
        NSString *loginUser = [[NSUserDefaults standardUserDefaults] valueForKey:@"currentUser"];
    
        AppDelegate *sharedDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        NSManagedObjectContext *context = [sharedDelegate managedObjectContext];
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"ThreadInfo"];
        [fetchRequest setReturnsObjectsAsFaults:NO];
    
        //Since there is every only one tell SQL to stop looking after one
        [fetchRequest setFetchBatchSize:1]; 
    
        /* Change these to your single constraint
        NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"userEmail ==   %@",loginUser];
        NSPredicate *threadPredicate = [NSPredicate predicateWithFormat:@"threadID == %@",inThreadID];
        NSPredicate *compoundPredicate = [NSCompoundPredicate andPredicateWithSubpredicates: @[userPredicate, threadPredicate]];
        [fetchRequest setPredicate:compoundPredicate];
        */
    
        NSError *error = nil;
        NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
        if (fetchedObjects == nil) {
            NSLog(@"Error fetching: %@\n%@", [error localizedDescription], [error userInfo]);
            abort();
        }
        return [fetchedObjects lastObject];
    }
    

    设置获取限制将导致存储停止查看第一个匹配项,这将减少获取时间。将其移动到单个字符串也可以减少获取时间。

    以后在这种情况下避免使用字符串。使用整数主键非常快,可以完全避免这个性能问题。

    【讨论】:

    • 先生,这就是问题所在,但我很困惑如何改进 fetch,因为我需要比较两个字符串。你能建议我一些方法或逻辑吗?
    • 方法真的很简单,不要对大量实体做两个字符串比较。那是非常昂贵的。重新设计数据。
    • 您可能可以通过将获取限制设置为 1(假设您正在寻找一个唯一的)来对其进行创可贴,看看是否可以改善它。您还可以在实体中索引这两个属性,但这两个都是止损。以后你的数据设计不好。
    • 异步获取将NOT帮助,因为获取是问题所在。它总是会阻止NSPersistentStoreCoordinator,这是您的核心问题。限制批量大小可能有帮助,但不能保证。你需要测试。你真正需要做的是修复你的数据结构。搜索两个字符串是无效的。一直是,永远都是。
    • 批量更新是在 iOS 8 而不是 iOS 7 中引入的。考虑到您遇到的问题,我建议使用该 API,因为它有许多相关的风险.
    【解决方案2】:

    面对coreData和multiplyThreads等问题,应在对应方法中添加如下代码:

    [[NSNotificationCenter defaultCenter] addObserver: cdm.managedObjectContext selector: @selector(mergeChangesFromContextDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object: nil];
    

    然后coreData在一个线程发生变化,它会立即在另一个正在使用coreData的线程中更新它。

    希望答案对你有所帮助!

    【讨论】:

    • 你的意思是在获取方法中?
    • 我的意思是你的崩溃可能是两个线程操作导致崩溃的 coreData。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-04
    • 2015-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-12
    • 1970-01-01
    相关资源
    最近更新 更多