【问题标题】:iOS - Crash on NSManagedObjectContextiOS - NSManagedObjectContext 崩溃
【发布时间】:2017-06-13 15:03:02
【问题描述】:

我遇到了一些我在测试、开发或使用过程中从未经历过的崩溃。

我可以在 Fabric Dashboard 上看到它们,它与 NSManagedObjectContext 有关。

这是对 StackTrace 的第一次调用:

CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:[MyAppDelegate mainContext]];

CDFavori 是一个代表 CoreData 对象的类,它被扩展以实现一些方法(为了获取):

+(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;
}

编辑 - 仅提及声明:

在标题中:

@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 文件。

但我必须更改所有变量,并更改只读属性?

【问题讨论】:

    标签: ios objective-c core-data nsmanagedobject nsmanagedobjectcontext


    【解决方案1】:

    我相信您有 3 个不同的问题:

    1. ARC 密谋反对你

    尝试替换:

    CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:[MyAppDelegate mainContext]];
    

    NSManagedObjectContext* context = [MyAppDelegate mainContext];
    CDFavori* fav = [CDFavori favoriWithIndicatif:homeFavoriteIndicatif context:context];
    

    我有一个类似的问题,这解决了它。原因是当上下文被创建然后直接作为参数传递 ARC 在下一行释放上下文。然后 managedObject 没有上下文并且它崩溃了。如果您首先将其分配给局部变量,那么 ARC 将在整个范围内保留上下文。这在调试环境中不会发生,因为 ARC 在那里的行为不同。

    1. 您没有正确执行多线程

    下一个问题是为什么要释放上下文。虽然您没有显示任何错误代码,但我怀疑这发生在应用程序生命周期的早期,并且有多个线程同时创建主上下文。因此,第一个调用创建一个上下文并将其分配给_managedObjectContext,然后分配第二个上下文并释放第一个上下文。 (并且它没有保留在本地范围内,因此会发生崩溃)。

    在您的核心数据设置中,您应该只访问主线程上的_managedObjectContext 变量。我建议在managedObjectContext方法的开头添加一个检查

    if (![NSThread mainThread]) {
        // log error to fabric
        //[[Crashlytics sharedInstance] recordError:...];
        return nil;
    }
    
    1. 懒惰地创建核心数据会导致错误

    另外,我会在 application:didFinishLaunchingWithOptions: 启动时显式创建 _managedObjectContext,而不是懒惰地创建它。当它被懒惰地创建时,你不知道它什么时候会被创建。如果它是从后台线程创建的,你的整个堆栈都会被搞砸。懒惰地做它并没有什么好处,因为你肯定会创建它以便向用户展示任何东西。 您可以保留您的代码并简单地添加

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
       [self managedObjectContext];  //force loading of context
    

    【讨论】:

    • 感谢您花时间正确理解我的问题。我从你的回答中学到了很多。我将您的建议 1 和 2 添加到我的代码中。关于惰性创建,是否也适用于 managedObjectModel 和 persistentStoreCoordinator ?
    • 是的。一开始就创建模型、存储和上下文要简单得多。您只需要保留一个指向上下文的指针。上下文保留了商店和模型。
    • 没有。这些类都不应使用init 创建。模型使用:initWithContentsOfURL,商店使用initWithManagedObjectModel,上下文使用initWithConcurrencyType
    • 啊哈,抱歉我没有直接理解。是的,我肯定会尝试的。谢谢乔恩。即使我真的不知道崩溃是否得到纠正,我也会给你接受的答案。但是你帮助我让它变得更加健壮。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 2013-01-24
    • 1970-01-01
    相关资源
    最近更新 更多