Ios 后台线程方法无法解决冻结问题
我有我的应用程序冻结问题,所以我使用仪器查找问题,发现问题与CoreData保存和获取有关。我尝试过后台coredata方法(父子、通知),但我的问题尚未解决。我也提到了,但不知道这个方法是如何在我的应用程序中实现的 仪器日志: 保存到数据库Ios 后台线程方法无法解决冻结问题,ios,multithreading,core-data,Ios,Multithreading,Core Data,我有我的应用程序冻结问题,所以我使用仪器查找问题,发现问题与CoreData保存和获取有关。我尝试过后台coredata方法(父子、通知),但我的问题尚未解决。我也提到了,但不知道这个方法是如何在我的应用程序中实现的 仪器日志: 保存到数据库 -(void)updateThreadEntityWithSyncDetails:(NSMutableDictionary *)inDictionary { NSString *loginUser=[[NSUserDefaults stand
-(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);
// }
// }];
// }];
}
获取
-(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];
}
}
}
//使用父-子上下文的方法
[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];
});
}]
[UIAppDelegate.childManagedObjectContext执行锁:^{
对于(int i=0;i
//使用父-子上下文的方法
[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];
});
}]
[UIAppDelegate.childManagedObjectContext执行锁:^{
对于(int i=0;i您是否尝试为操作使用单独的上下文,并在保存后将其与mainContext合并
例如
导入完成后,保存localContext
NSError * error = nil;
if (![[self localContext] save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
这将把数据写入PersistentStore
现在需要将PersistentStore中的更改告知mainContext。这是通过观察通知来完成的
e、 g
这是对通知作出反应的方法:
- (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更具线程安全性。您是否尝试为操作使用单独的上下文,并在保存后将其与mainContext合并
例如
导入完成后,保存localContext
NSError * error = nil;
if (![[self localContext] save:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
这将把数据写入PersistentStore
现在需要将PersistentStore中的更改告知mainContext。这是通过观察通知来完成的
e、 g
这是对通知作出反应的方法:
- (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更安全。首先,感谢您立即提供代码片段和跟踪。这非常有用
所以,看看跟踪和代码
-updateThreadEntityWithSyncDetails:
正在主线程上调用,占花费时间的33%。不太好
您正在为错误传递nil
:
永远不要这样做。始终传递错误指针并检查调用结果以查看是否有错误
这个条件可以更简洁:
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不是字符串,则反转提取。否则,这只是糟糕的数据模型设计,不存在错误除了修好那道墙外,不会有什么帮助的
另一个真正慢的地方是-检查ReadDetailsForThreadId:
的存在性。您没有发布该方法,但我怀疑这是同一个问题,您的获取花费了大量时间,并且它位于主队列上
总的来说,这个设计很差,需要重新处理。您正在比较数据存储中的字符串,这是检索数据最慢的方法之一。您也在主线程上,处理看起来不需要在主线程上的内容
请记住黄金法则,如果用户未对数据进行操作,则操作不得在主队列上。没有例外。首先,感谢您立即提供代码片段和跟踪。这非常有用
所以,看看跟踪和代码
-updateThreadEntityWithSyncDetails:
正在主线程上调用,占花费时间的33%。不太好
您正在为错误传递nil
:
永远不要这样做。始终传递错误指针并检查调用结果以查看是否有错误
这个条件可以更简洁:
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不是字符串,则反转提取。否则,这只是糟糕的数据模型设计,不存在错误除了修好那道墙外,不会有什么帮助的
另一个真正慢的地方是-检查ReadDetailsForThreadId:
的存在性。您没有发布该方法,但我怀疑这是同一个问题,您的获取花费了大量时间,并且它位于主队列上
总的来说,这个设计很差,需要重新处理。您正在比较数据存储中的字符串,这是检索数据最慢的方法之一。您也在主线程上,处理看起来不需要在主线程上的内容
记住这条黄金法则,如果数据没有被用户操纵,那么操纵就不能在主队列上。没有例外。可能是因为使用了两个上下文,如果使用三个上下文会怎么样?workContext Maincontext ChildContext。你知道如何实现这一个吗?我尝试过这个方法,但仍然没有成功应用程序正在冻结。在保存之前,存在一些获取操作,这也会导致问题,您可以查看我的探查器日志。我如何有效地使用这两个工具?这篇文章解释得很好。另外,我已经编辑了代码。请签入您的代码为什么两次保存mainObjectcontext?如果我们已经使用通知,那么为什么需要[UIAppDelegate.managedObjectContext保存:无];?抱歉