rob*_*cer 9 cocoa-touch core-data cllocationmanager
我一直在尝试CLLocationManager的startMonitoringSignificantLocationChanges,我碰到的一些问题与核心数据.事实证明,自iOS 5.0以来,Core Data默认使用NSFileProtectionCompleteUntilFirstUserAuthentication.这意味着如果设置了密码,则持久存储在设备打开时不可用,直到首次输入密码为止.如果您正在使用位置更新,那么您的应用可能会在此期间启动,并且Core Data将尝试加载持久存储时出错.
显然,转换NSFileProtectionNone是解决这个问题的最简单方法.我不想这样做 - 我没有在数据库中存储任何超级敏感的东西,但这些位置更新也不是超级关键.
我知道我可以[[UIApplication sharedApplication] isProtectedDataAvailable]用来检查数据是否已经解锁,并且我可以applicationProtectedDataWillBecomeUnavailable:在我的应用程序委托中使用它一旦解锁就做出适当的响应.这对我来说似乎很麻烦 - 我必须添加一些额外的检查,以确保如果持久存储不可用则没有出错,一旦它变得可用就重新设置一堆东西,等等.而额外的代码并没有提供太多的好处 - 如果它在这种状态下启动,应用程序仍然无法执行任何操作.
所以我想我只是不确定哪种方式更合适":
NSFileProtectionNone.applicationProtectedDataWillBecomeUnavailable:在用完之后再次设置.[[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)并且受保护的数据不可用([[UIApplication sharedApplication] isProtectedDataAvailable] == NO))只需调用exit(0)(或类似的东西)退出应用程序.一方面,这似乎是最简单的解决方案,我没有看到任何缺点.但它似乎......"错了"?我想我无法决定它是一个干净的解决方案还是一个懒惰的解决方案.经过一段时间的思考后,我想出了一个我很满意的解决方案.该exit(0)选项需要考虑的一件事是,如果用户需要一段时间来解锁设备,应用程序可能会不断加载,退出和重新加载.然而,如果你只是阻止应用程序做很多事情,它可能只需要加载一次,并且很可能会更有效率.所以我决定尝试我的选项3,看看它到底有多乱.事实证明这比我想象的要简单.
首先,我BOOL setupComplete向我的应用代表添加了一个属性.这让我可以轻松地检查应用程序是否在各个点完全启动.然后在application:didFinishLaunchingWithOptions:我尝试初始化托管对象上下文,然后执行以下操作:
NSManagedObjectContext *moc = [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
} else {
UIApplication *app = [UIApplication sharedApplication];
if ([app applicationState] == UIApplicationStateBackground && ![app isProtectedDataAvailable]) {
[app beginIgnoringInteractionEvents];
} else [self presentErrorWithTitle:@"There was an error opening the database."];
}
Run Code Online (Sandbox Code Playgroud)
setupWithManagedObjectContext:只是一个完成设置的自定义方法.我不确定beginIgnoringInteractionEvents是否有必要,但我把它添加到了安全的一面.这样当应用程序被带到前面时,我可以确定界面被冻结,直到设置完成.如果急切的用户焦急地窃听,它可能会避免崩溃.
然后applicationProtectedDataDidBecomeAvailable:我打电话给这样的事情:
if (!self.setupComplete) {
NSManagedObjectContext *moc = [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
UIApplication *app = [UIApplication sharedApplication];
if ([app isIgnoringInteractionEvents]) [app endIgnoringInteractionEvents];
} else [self presentErrorWithTitle:@"There was an error opening the database."];
}
Run Code Online (Sandbox Code Playgroud)
完成设置并重新启用界面.这是大部分工作,但您还需要检查其他代码,以确保在持久存储可用之前不会调用任何依赖于Core Data的内容.有一点要注意的是,applicationWillEnterForeground和applicationDidBecomeActive可能会调用之前applicationProtectedDataDidBecomeAvailable,如果用户从这个背景状态启动应用程序.所以我在各个地方添加if (self.setupComplete) { … }了以确保在它准备好之前没有运行.我还有几个地方,我需要在加载数据库后刷新界面.
为了(部分)测试这个没有大量的驱动,我暂时修改application:didFinishLaunchingWithOptions:为不设置数据库:
NSManagedObjectContext *moc = nil; // [self managedObjectContext];
if (moc) {
self.setupComplete = YES;
[self setupWithManagedObjectContext:moc];
} else {
UIApplication *app = [UIApplication sharedApplication];
// if ([app applicationState] == UIApplicationStateBackground && ![app isProtectedDataAvailable]) {
[app beginIgnoringInteractionEvents];
// } else [self presentErrorWithTitle:@"There was an error opening the database."];
}
Run Code Online (Sandbox Code Playgroud)
然后我将代码applicationProtectedDataDidBecomeAvailable:移到了applicationWillEnterForeground:.这样我可以启动应用程序,确保没有任何意外情况发生,按主页按钮,再次打开应用程序,并确保一切正常.由于实际代码需要移动一个很长的距离并且每次等待五分钟,这给了我一个很好的方法来估计正在发生的事情.
绊倒我的最后一件事是我的持久商店协调员.典型的实现可能如下所示:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Test.sqlite"];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
Run Code Online (Sandbox Code Playgroud)
这基于Apple的示例代码,它在评论中解释说您需要适当地处理错误.我自己的代码比这更多,但我没有考虑的一件事是,如果加载持久存储时出错,这将返回一个非零结果!这允许我的所有其他代码继续进行,就像它正常工作一样.即使再次调用persistentStoreCoordinator,它也只会在没有有效存储的情况下返回相同的协调器,而不是再次尝试加载存储.有多种方法可以解决这个问题,但对我而言,除非能够添加商店,否则最好不要设置_persistentStoreCoordinator:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) return _persistentStoreCoordinator;
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Test.sqlite"];
NSError *error = nil;
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if ([coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
_persistentStoreCoordinator = coordinator;
} else {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
return _persistentStoreCoordinator;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1416 次 |
| 最近记录: |