【问题标题】:iOS 5 Core Data freezeiOS 5 核心数据冻结
【发布时间】:2011-12-10 02:44:51
【问题描述】:

我尝试做以下简单的事情:

NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];

没什么特别的。但这在 iOS 5 中冻结,在 iOS 4 中运行良好。我没有收到异常、警告或错误;我的应用只是冻结了。

请帮帮我!我要死在这里了! ;)

【问题讨论】:

  • 它是冻结还是崩溃?它不能两者兼得。如果它崩溃了,会有一个崩溃日志或堆栈跟踪。
  • 你没有在线程之间共享上下文,是吗?
  • @Dries - 我也在调查这个原因,我发现了同样的行为。如果您在 4.3 模拟器上运行,它可以正常工作吗(我的可以)?在 5.0 模拟器上,它会定期锁定,没有错误消息或调试信息。
  • 我还没有答案,但我发现当它挂起时,如果你在调试器下运行并且你破坏了应用程序,它会在 executeFetchRequest 行停止。如果您随后检查您的案例中的 managedObjectContext(上下文),我发现其中一个内部变量 _objectStoreLockCount 设置为 2。这告诉我它正在等待锁定。
  • 这绝对是 iOS5 现在处理并发方式的一个问题 - 我仔细查看了我的代码,但它看起来并不漂亮

标签: iphone core-data ios5


【解决方案1】:

我不知道你是否也使用不同的线程。如果是,则问题来自 NSManagedObjects 本身不是线程安全的事实。在主线程上创建一个 ManagedContext 并在另一个线程上使用它会冻结线程。

也许这篇文章可以帮助你: http://www.cimgf.com/2011/05/04/core-data-and-threads-without-the-headache/

Apple 有一个演示应用程序,用于在多个线程(通常是主线程和后台线程)上处理 Coredata:http://developer.apple.com/library/ios/#samplecode/TopSongs/Introduction/Intro.html

我为解决这个问题所做的是:

  • 在应用程序委托中:创建持久存储(所有线程一个)并为主线程创建 Coredata 托管上下文,
  • 在后台线程中,创建一个新的托管上下文(来自同一个持久存储)
  • 保存时使用通知,让 mainContext 知道后台线程何时完成(插入行或其他)。

有几种解决方案,使用 NSQueueOperation。就我而言,我正在使用 while 循环。这是我的代码,如果它可以帮助你。不过,Apple 的并发文档及其热门歌曲示例应用程序是不错的起点。

在应用程序委托中:

 -(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    self.cdw = [[CoreDataWrapper alloc] initWithPersistentStoreCoordinator:[self persistentStoreCoordinator] andDelegate:self];
    remoteSync = [RemoteSync sharedInstance];

    ...

    [self.window addSubview:navCtrl.view];
    [viewController release];

    [self.window makeKeyAndVisible];
    return YES; 
}    

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (persistentStoreCoordinator == nil) {
        NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
        NSLog(@"Core Data store path = \"%@\"", [storeUrl path]);
        persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
        NSError *error = nil;
        NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
        NSAssert3(persistentStore != nil, @"Unhandled error adding persistent store in %s at line %d: %@", __FUNCTION__, __LINE__, [error localizedDescription]);
    }
    return persistentStoreCoordinator;
}


-(NSManagedObjectContext *)managedObjectContext {
    if (managedObjectContext == nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
    }
    return managedObjectContext;
}

-(NSPersistentStoreCoordinator *)persistentStoreCoordinator {
    if (persistentStoreCoordinator == nil) {
        NSURL *storeUrl = [NSURL fileURLWithPath:self.persistentStorePath];
        NSLog(@"Core Data store path = \"%@\"", [storeUrl path]);
        persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[NSManagedObjectModel mergedModelFromBundles:nil]];
        NSError *error = nil;
        NSPersistentStore *persistentStore = [persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&error];
        NSAssert3(persistentStore != nil, @"Unhandled error adding persistent store in %s at line %d: %@", __FUNCTION__, __LINE__, [error localizedDescription]);
    }
    return persistentStoreCoordinator;
}


-(NSManagedObjectContext *)managedObjectContext {
    if (managedObjectContext == nil) {
        managedObjectContext = [[NSManagedObjectContext alloc] init];
        [managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator];
    }
    return managedObjectContext;
}


-(NSString *)persistentStorePath {
    if (persistentStorePath == nil) {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsDirectory = [paths lastObject];
        persistentStorePath = [[documentsDirectory stringByAppendingPathComponent:@"mgobase.sqlite"] retain];
    }
    return persistentStorePath;
}

-(void)importerDidSave:(NSNotification *)saveNotification {
    if ([NSThread isMainThread]) {
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:saveNotification];
    } else {
        [self performSelectorOnMainThread:@selector(importerDidSave:) withObject:saveNotification waitUntilDone:NO];
    }
}

在运行后台线程的对象中:

monitor = [[NSThread  alloc] initWithTarget:self selector:@selector(keepMonitoring) object:nil];

-(void)keepMonitoring{
    while(![[NSThread currentThread]  isCancelled]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    
        AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

        //creating the cdw here will create also a new managedContext on this particular thread
        cdwBackground = [[CoreDataWrapper alloc] initWithPersistentStoreCoordinator:appDelegate.persistentStoreCoordinator andDelegate:appDelegate];
        ...
    }
}

希望对您有所帮助,

M.

【讨论】:

    【解决方案2】:

    我遇到了同样的问题。如果您在调试器下运行并且当应用程序“挂起”时停止应用程序(使用调试器上的“暂停”按钮。如果您在 executeFetchRequest 行,请检查上下文变量。如果它有一个 ivar _objectStoreLockCount 及其大于 1,则等待关联存储上的锁。

    您在关联商店中创建竞态条件的地方。

    【讨论】:

    • 超级好小费费迪尔!谢谢!
    【解决方案3】:

    这听起来真的像是试图从创建它的线程/队列之外的线程/队列访问NSManagedObjectContext。正如其他人建议的那样,您需要查看您的线程并确保您遵循 Core Data 的规则。

    【讨论】:

      【解决方案4】:

      感谢本页提供的有关如何解决从 iOS4 升级时出现的冻结问题的提示。这是我开始在 iOS 上编程以来发现的最烦人的问题。

      我找到了一种快速解决方案,可以解决其他线程对上下文的少量调用的情况。

      我只使用 performSelectorOnMainThread

          [self performSelectorOnMainThread:@selector(stateChangeOnMainThread:) withObject: [NSDictionary dictionaryWithObjectsAndKeys:state, @"state", nil] waitUntilDone:YES];
      

      要检测从另一个线程调用上下文的位置,您可以在调用上下文的函数上的 NSLog 上放置一个断点,如下面的代码所示,然后只需使用 performSelectorOnMainThread他们。

      if(![NSThread isMainThread]){
           NSLog(@"Not the main thread...");
      }
      

      我希望这可能会有所帮助...

      【讨论】:

      • 我得到了同样的冻结,但我绝对没有线程任何东西所以调用 performSelectorOnMainThread 不会改变任何东西。
      • 好吧,也许这是一个不同的问题。如果您在应用程序冻结时暂停调试器,您将看到它停止的地方。如果您可以从与线程 1 不同的线程访问 CoreDataModel 上下文,那么这就是问题所在,您可以通过这种方式修复它。否则,您可能需要进一步搜索问题才能识别和解决问题。
      【解决方案5】:

      执行获取请求必须从创建上下文的线程发生。

      记住它不是线程安全的,尝试从另一个线程executeFetchRequest 会导致不可预知的行为。

      为了正确执行此操作,请使用

      [context performBlock: ^{
           NSArray * entities = [context executeFetchRequest:inFetchRequest error:&fetchError];   
      }];
      

      这将executeFetchRequest 与上下文在同一线程中,这可能是也可能不是主线程。

      【讨论】:

        【解决方案6】:

        在我的情况下,应用程序会在“executeFetchRequest”之前冻结而没有任何警告。解决方案是将所有数据库操作包装在 @synchronized(persistentStore) 中。 例如:

        NSArray *objects;
        @synchronized([self persistentStoreCoordinator]) {
                    objects = [moc executeFetchRequest:request error:&error];
        }
        

        【讨论】:

          【解决方案7】:

          使用 fetchrequest 删除所有对象对我不起作用,sqlite 看起来已损坏。我找到的唯一方法是

              //Erase the persistent store from coordinator and also file manager.
              NSPersistentStore *store = [self.persistentStoreCoordinator.persistentStores lastObject];
              NSError *error = nil;
              NSURL *storeURL = store.URL;
              [self.persistentStoreCoordinator removePersistentStore:store error:&error];
              [[NSFileManager defaultManager] removeItemAtURL:storeURL error:&error];
          
              //Make new persistent store for future saves   (Taken From Above Answer)
              if (![self.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
                  // do something with the error
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-02-18
            • 1970-01-01
            • 2019-10-08
            • 2013-11-26
            • 1970-01-01
            • 2016-03-20
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多