后台线程方法不解决冻结问题

时间:2015-05-21 04:08:15

标签: ios multithreading core-data

我有我的应用程序冻结问题,所以我使用Instruments找到问题,发现问题与CoreData保存和获取有关。我尝试过背景coredata方法(父子,通知),但我的问题一直没有解决。我也提到http://martiancraft.com/blog/2015/03/core-data-stack/,但不知道这个方法在我的应用程序中是如何实现的。

仪器日志: https://www.dropbox.com/s/agjtw1wqubsgwew/Instruments9.trace.zip?dl=0

保存到数据库

  -(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:context];
[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 = [context 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);
   //     }
   //            }];
   // }];


 }    

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)updateSolicitationWithSyncDetails:(NSDictionary *)inDictionary
 {    
NSMutableDictionary *paramDict=[NSMutableDictionary dictionaryWithDictionary:inDictionary];
NSString *userEmail=[[NSUserDefaults standardUserDefaults] valueForKey:@"currentUser"];

[paramDict setObject:[NSNumber numberWithBool:NO] forKey:@"isSystemMessage"];
[paramDict setObject:userEmail forKey:@"userEmail"];

if([[inDictionary allKeys] containsObject:@"owned"])
{
   BOOL isDuplicate=[[IXDataBaseManager sharedNetworkDataManager] checkForExistenceOfThreadDetailsForSolicitationID:[inDictionary objectForKey:@"solicitation"]];// FETCH
    if(!isDuplicate)
    {
        int randomIndex=[[IXNetworkDataManager sharedNetworkDataManager] getIndexForColorImageForTab:@"OUT"];
        [paramDict setObject:message forKey:@"threadDescription"];
        [paramDict setObject:[NSNumber numberWithInt:randomIndex] forKey:@"colorCode"];

     BOOL isDuplicateVal=[[IXDataBaseManager sharedNetworkDataManager] checkForExistenceOfSolicitationID:[inDictionary objectForKey:@"solicitation"]];// FETCH
   [paramDict setObject:message forKey:@"threadDescription"];

  ThreadInfo *threadInfo=[[IXDataBaseManager sharedNetworkDataManager] retrieveSolicitationInfoForThreadID:[inDictionary objectForKey:@"solicitation"]];
  [paramDict setObject:threadInfo.threadID forKey:@"thread"];
  [[IXDataBaseManager sharedNetworkDataManager] updateThreadEntityWithSyncDetails:paramDict];
    }
}
}

3 个答案:

答案 0 :(得分:5)

首先,感谢您立即制作代码片段和跟踪。这非常有帮助。

所以,看看跟踪和代码。

在主线程上调用

-updateThreadEntityWithSyncDetails:,并且花费了33%的时间。不好。

您正在通过nil error: 从不这样做。始终传递错误指针并检查调用结果以查看是否存在错误。

这个条件可以更清洁:

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

考虑:

if (inDictionary[@"userEmail"] != nil && inDictionary[@"userEmail"] != [NSNull null]) {
    threadInfo.userEmail = inDictionary[@"userEmail"];
}

更容易阅读。

重新编写该方法将使主线程完成工作:

- (void)updateThreadEntityWithSyncDetails:(NSMutableDictionary*)inDictionary
{
    NSString *loginUser = [[NSUserDefaults standardUserDefaults] valueForKey:@"currentUser"];

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

    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [context setParentContext:[sharedDelegate managedObjectContext]];

    [context performBlock:^{
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"ThreadInfo"];
        [fetchRequest setReturnsObjectsAsFaults:NO];
        [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"userEmail == %@ && threadID == %@",loginUser, inDictionary[@"thread"]];

        NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:nil];
        for (ThreadInfo *threadInfo in fetchedObjects) {
            if (inDictionary[@"userEmail"] != nil && inDictionary[@"userEmail"] != [NSNull null]) {
                threadInfo.userEmail = inDictionary[@"userEmail"];
            }
            if (inDictionary[@"badgeValue"] != nil && inDictionary[@"badgeValue"] != [NSNull null]) {
                threadInfo.badgeValue = inDictionary[@"badgeValue"];
            }
            if (inDictionary[@"choice4Percentage"] != nil && inDictionary[@"choice4Percentage"] != [NSNull null]) {
                threadInfo.choice4Percentage = inDictionary[@"choice4Percentage"];
            }
            if (inDictionary[@"choice5Percentage"] != nil && inDictionary[@"choice5Percentage"] != [NSNull null]) {
                threadInfo.choice5Percentage = inDictionary[@"choice5Percentage"];
            }
        }

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

        }
    }];
}

但是它可能仍会阻塞主线程,因为我猜测大部分CPU时间都在执行此提取。获取速度很慢。在该获取中有两个字符串比较。这很糟糕,应予以纠正。如果ThreadID不是字符串,则反转获取。否则这只是糟糕的数据模型设计,除了修复fetch之外没有太多帮助。

你的另一个非常慢点在于-checkForExistenceOfThreadDetailsForThreadID:。您没有发布该方法,但我怀疑它是同一个问题,您的提取会花费您大量的时间并且它在主队列中。

总的来说,这种设计很差,需要重新设计。您正在比较数据存储中的字符串,这是检索数据的最慢方法之一。对于看起来不需要在主线程上的东西,你也是主线程。

记住黄金法则,如果用户没有操纵数据,那么操作绝不能在主队列上。没有例外。

答案 1 :(得分:0)

 //Your Parent context init

- (NSManagedObjectContext *)managedObjectContext
{
if (_managedObjectContext != nil) {
    return _managedObjectContext;
}

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
    _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    [_managedObjectContext setPersistentStoreCoordinator:coordinator];
     [_managedObjectContext setUndoManager:nil];
}
return _managedObjectContext;
 }

- (NSManagedObjectContext *)childManagedObjectContext 
{
if (_childManagedObjectContext != nil) {
    return _childManagedObjectContext;
}

NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil) {
    _childManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [_childManagedObjectContext setPersistentStoreCoordinator:coordinator];
    [_childManagedObjectContext setUndoManager:nil];
       [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_mocDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:nil];
}


return _childManagedObjectContext;
}
- (void)_mocDidSaveNotification:(NSNotification *)notification
   {
NSManagedObjectContext *savedContext = [notification object];

// ignore change notifications for the main MOC
if (_managedObjectContext == savedContext)
{
    return;
}

if (_managedObjectContext.persistentStoreCoordinator != savedContext.persistentStoreCoordinator)
{
    // that's another database
    return;
}

dispatch_sync(dispatch_get_main_queue(), ^{
    [_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
});
 }

//使用父子上下文的方式

[UIAppDelegate.childManagedObjectContext performBlock:^{


         for (int i=0; i<count; i++) {
             //Fill Database and save it in child context first for each entry and save it in child context
             [UIAppDelegate.childManagedObjectContext save:nil];
        }

        dispatch_sync(dispatch_get_main_queue(), ^{

         //Finally when u have inserted all entries save in the parent context
            [UIAppDelegate.managedObjectContext save:nil];



        });

    }]

//你的代码对于理解逻辑非常重要,但如果你没有尝试过这种方式,那就试试吧

答案 2 :(得分:0)

您是否尝试为操作使用单独的Context并在保存后将其与 mainContext 合并?

E.g。

NSManagedObjectContext * localContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[localContext setPersistentStoreCoordinator:[[self managedObjectContext] persistentStoreCoordinator]];
导入完成后,您可以保存localContext

NSError * error = nil;
if (![[self localContext] save:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}

这会将您的数据写入PersistentStore。

现在需要通知您的 mainContext 有关PersistentStore中的更改。这是通过观察通知来完成的。

e.g。

 [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(didSaveNotification:)
                                              name:NSManagedObjectContextDidSaveNotification
                                            object:nil];

这是对通知作出反应的方法:

- (void)didSaveNotification:(NSNotification *)notification
{
    //    NSLog(@"%s %@\n",__PRETTY_FUNCTION__,[notification userInfo]);
    NSManagedObjectContext * mainContext = [self managedObjectContext];
    void (^mergeChanges)(void)=^{
        [mainContext mergeChangesFromContextDidSaveNotification:notification];
    };
    if([NSThread isMainThread]) {
        mergeChanges();
    } else {
        dispatch_sync(dispatch_get_main_queue(), mergeChanges);
    }
}

使用不同的NSManagedContext处理相同的PersistentStore比使用单个NSManagedContext更加线程安全。