iOS - NSManagedObjectContext 崩溃
iOS - Crash on NSManagedObjectContext
我遇到了一些我在测试、开发或使用过程中从未遇到过的崩溃。
我可以在 Fabric Dashboard 上看到它们,它涉及 NSManagedObjectContext。
这是对 StackTrace 的第一次调用:
CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:[MyAppDelegate mainContext]];
CDFavori 是一个 class 表示 CoreData object,它被扩展以实现一些方法(为了获取):
+(CDFavori *)favoriWithIndicatif:(NSString*)indicatif context:(NSManagedObjectContext*)context
{
if (nil == indicatif || nil == context)
return nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"CDFavori"];
[request setPredicate:[NSPredicate predicateWithFormat:@"indicatif LIKE %@", indicatif]];
NSError *error = nil;
NSArray *favoris = [context executeFetchRequest:request error:&error];
CDFavori *fav = nil;
if (nil != error) {
DDLogError(@"Error = %@ (%@)", indicatif, error);
} else if (0 < [favoris count])
{
fav = [favoris objectAtIndex:0];
if (1 < [favoris count]) {
DDLogWarn(@"More than one object present in DB : %@", indicatif);
}
}
return favori;
}
崩溃不是来自此方法,它只是为您提供一些背景信息。
问题来自 AppDelegate 和 NSManagedObjectContext。
这是我的核心数据方法代码:
+(NSManagedObjectContext*)mainContext
{
return ((MyAppDelegate*)[UIApplication sharedApplication].delegate).managedObjectContext;
}
崩溃在这里:
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
编辑 - 仅提及声明:
在 header 中:
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
在 .m 文件中:
#pragma mark - Core Data stack
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
回答后编辑:
你觉得这样的东西会更好吗?
删除此声明:
#pragma mark - Core Data stack
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
并将其替换为:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
_managedObjectModel = [[NSManagedObjectModel alloc] init];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] init];
}
这样会更好?使用相同的 .h 文件。
但我必须更改所有变量,并更改只读属性?
我认为您有 3 个不同的问题:
- ARC 密谋反对你
尝试替换:
CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:[MyAppDelegate mainContext]];
和
NSManagedObjectContext* context = [MyAppDelegate mainContext];
CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:context];
我有一个类似的问题,这解决了它。原因是当上下文被创建然后直接作为参数传递给ARC时在下一行释放上下文。然后 managedObject 没有上下文并且它崩溃了。如果您首先将它分配给一个局部变量,那么 ARC 将为整个范围保留上下文。这在调试环境中不会发生,因为 ARC 在那里的行为不同。
- 您没有正确执行多线程处理
下一个问题是为什么要释放上下文。虽然您没有显示任何错误的代码,但我怀疑这是在应用程序生命周期的早期发生的,并且有多个线程同时创建主上下文。因此,第一个调用创建一个上下文并将其分配给 _managedObjectContext
,然后分配第二个上下文并释放第一个上下文。 (并且它没有保留在本地范围内,所以会发生崩溃)。
在您的核心数据设置中,您应该只访问主线程上的 _managedObjectContext
变量。我建议在 managedObjectContext
方法的开头添加一个检查
if (![NSThread mainThread]) {
// log error to fabric
//[[Crashlytics sharedInstance] recordError:...];
return nil;
}
- 延迟创建核心数据会导致错误
此外,我会在 application:didFinishLaunchingWithOptions:
启动时明确创建 _managedObjectContext
,而不是懒惰地创建它。当它被懒惰地创建时,你不知道它什么时候会被创建。如果它是从后台线程创建的,你的整个堆栈就会乱七八糟。懒惰地执行它几乎没有什么好处,因为您肯定会创建它以便向用户显示任何内容。
您可以按原样保留代码,只需添加
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self managedObjectContext]; //force loading of context
我遇到了一些我在测试、开发或使用过程中从未遇到过的崩溃。
我可以在 Fabric Dashboard 上看到它们,它涉及 NSManagedObjectContext。
这是对 StackTrace 的第一次调用:
CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:[MyAppDelegate mainContext]];
CDFavori 是一个 class 表示 CoreData object,它被扩展以实现一些方法(为了获取):
+(CDFavori *)favoriWithIndicatif:(NSString*)indicatif context:(NSManagedObjectContext*)context
{
if (nil == indicatif || nil == context)
return nil;
NSFetchRequest *request = [[NSFetchRequest alloc] initWithEntityName:@"CDFavori"];
[request setPredicate:[NSPredicate predicateWithFormat:@"indicatif LIKE %@", indicatif]];
NSError *error = nil;
NSArray *favoris = [context executeFetchRequest:request error:&error];
CDFavori *fav = nil;
if (nil != error) {
DDLogError(@"Error = %@ (%@)", indicatif, error);
} else if (0 < [favoris count])
{
fav = [favoris objectAtIndex:0];
if (1 < [favoris count]) {
DDLogWarn(@"More than one object present in DB : %@", indicatif);
}
}
return favori;
}
崩溃不是来自此方法,它只是为您提供一些背景信息。
问题来自 AppDelegate 和 NSManagedObjectContext。
这是我的核心数据方法代码:
+(NSManagedObjectContext*)mainContext
{
return ((MyAppDelegate*)[UIApplication sharedApplication].delegate).managedObjectContext;
}
崩溃在这里:
- (NSManagedObjectContext *)managedObjectContext {
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
编辑 - 仅提及声明:
在 header 中:
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
在 .m 文件中:
#pragma mark - Core Data stack
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
回答后编辑:
你觉得这样的东西会更好吗?
删除此声明:
#pragma mark - Core Data stack
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
并将其替换为:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
_managedObjectModel = [[NSManagedObjectModel alloc] init];
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] init];
}
这样会更好?使用相同的 .h 文件。
但我必须更改所有变量,并更改只读属性?
我认为您有 3 个不同的问题:
- ARC 密谋反对你
尝试替换:
CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:[MyAppDelegate mainContext]];
和
NSManagedObjectContext* context = [MyAppDelegate mainContext];
CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:context];
我有一个类似的问题,这解决了它。原因是当上下文被创建然后直接作为参数传递给ARC时在下一行释放上下文。然后 managedObject 没有上下文并且它崩溃了。如果您首先将它分配给一个局部变量,那么 ARC 将为整个范围保留上下文。这在调试环境中不会发生,因为 ARC 在那里的行为不同。
- 您没有正确执行多线程处理
下一个问题是为什么要释放上下文。虽然您没有显示任何错误的代码,但我怀疑这是在应用程序生命周期的早期发生的,并且有多个线程同时创建主上下文。因此,第一个调用创建一个上下文并将其分配给 _managedObjectContext
,然后分配第二个上下文并释放第一个上下文。 (并且它没有保留在本地范围内,所以会发生崩溃)。
在您的核心数据设置中,您应该只访问主线程上的 _managedObjectContext
变量。我建议在 managedObjectContext
方法的开头添加一个检查
if (![NSThread mainThread]) {
// log error to fabric
//[[Crashlytics sharedInstance] recordError:...];
return nil;
}
- 延迟创建核心数据会导致错误
此外,我会在 application:didFinishLaunchingWithOptions:
启动时明确创建 _managedObjectContext
,而不是懒惰地创建它。当它被懒惰地创建时,你不知道它什么时候会被创建。如果它是从后台线程创建的,你的整个堆栈就会乱七八糟。懒惰地执行它几乎没有什么好处,因为您肯定会创建它以便向用户显示任何内容。
您可以按原样保留代码,只需添加
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self managedObjectContext]; //force loading of context