使用核心数据破坏多线程

Posted

技术标签:

【中文标题】使用核心数据破坏多线程【英文标题】:Broken Multithreading With Core Data 【发布时间】:2010-04-01 22:00:12 【问题描述】:

这是较早问题的一个更集中的版本,涉及与以前完全不同的主题。

我正在开发一个具有多个线程的 Cocoa Core Data 应用程序。有歌有艺人;每首歌曲都有艺术家关系。有一个委托代码文件,这里没有引用;它或多或少看起来像 XCode 生成的模板。

我使用前一种技术比使用后者好得多,而且任何多线程功能都来自 Core Data 模板。当我以一种方法完成所有 ManagedObjectContext 工作时,我很好。当我将 fetch-or-insert-then-return-object 工作放入单独的方法中时,应用程序会在新方法的 return 语句处暂停(但不会崩溃),如下所示。新方法甚至让自己的 MOC 变得安全,但它没有任何帮助。结果是对 Song 进行了一次添加,并在生成 Artist 后停止。

我没有收到任何错误或异常,但我不知道为什么。我已经调试了wazoo。我的理论是发生的任何错误都在另一个线程中,而我正在观看的错误是永远在等待某事。

我在使用 getArtistObject: 时做错了什么,我该如何解决?谢谢。

- (void)main

    NSInteger songCount = 1;
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
    [moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];

/* songDict generated here */

    for (id key in songDict)
    
        NSManagedObject *song = [NSEntityDescription insertNewObjectForEntityForName:@"Song" inManagedObjectContext:moc];
        [song setValue:[songDictItem objectForKey:@"Name"] forKey:@"title"];
        [song setValue:[self getArtistObject:(NSString *) [songDictItem objectForKey:@"Artist"]] forKey:@"artist"];

        [songDictItem release];

        songCount++;
    

    NSError *error;
    if (![moc save:&error])
    [NSApp presentError:error];

    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:moc];
    [moc release], moc = nil;
    [[self delegate] importDone];


- (NSManagedObject*) getArtistObject:(NSString*)theArtist

    NSError *error = nil;
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
    [moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];

    NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Artist" inManagedObjectContext:moc];
    [fetch setEntity:entityDescription];

    // object to be returned
    NSManagedObject *artistObject = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:moc];

    // set predicate (artist name)
    NSPredicate *pred = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"name = \"%@\"", theArtist]];
    [fetch setPredicate:pred];

    NSArray *response = [moc executeFetchRequest:fetch error:&error];

    if (error)
    [NSApp presentError:error];

    if ([response count] == 0) // empty resultset --> no artists with this name
    
    [artistObject setValue:theArtist forKey:@"name"];
    NSLog(@"%@ not found. Adding.", theArtist);

    return artistObject;
    
    else
    return [response objectAtIndex:0];


@end

【问题讨论】:

developer.apple.com/library/content/documentation/Cocoa/… 【参考方案1】:

尝试在访问之前锁定托管对象上下文,并在完成后再次解锁:

- (void)main

    NSInteger songCount = 1;
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
    [moc lock];
    [moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];
     [moc unlock];

/* songDict generated here */

    for (id key in songDict)
    
        [moc lock];
        NSManagedObject *song = [NSEntityDescription insertNewObjectForEntityForName:@"Song" inManagedObjectContext:moc];
        [moc unlock];
        [song setValue:[songDictItem objectForKey:@"Name"] forKey:@"title"];
        [song setValue:[self getArtistObject:(NSString *) [songDictItem objectForKey:@"Artist"]] forKey:@"artist"];

        [songDictItem release];

        songCount++;
    

    NSError *error;
    [moc lock];
    if (![moc save:&error])
    [NSApp presentError:error];
    [moc unlock];

    [moc lock];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:moc];
    [moc unlock];
    [moc release], moc = nil;
    [[self delegate] importDone];


- (NSManagedObject*) getArtistObject:(NSString*)theArtist

    NSError *error = nil;
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
    [moc lock];
    [moc setPersistentStoreCoordinator:[[self delegate] persistentStoreCoordinator]];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextDidSave:) name:NSManagedObjectContextDidSaveNotification object:moc];
    [moc unlock];

    NSFetchRequest *fetch = [[[NSFetchRequest alloc] init] autorelease];
    [moc lock];
    NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Artist" inManagedObjectContext:moc];
    [moc unlock];
    [fetch setEntity:entityDescription];

    // object to be returned
    [moc lock];
    NSManagedObject *artistObject = [[NSManagedObject alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:moc];
    [moc unlock];

    // set predicate (artist name)
    NSPredicate *pred = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"name = \"%@\"", theArtist]];
    [fetch setPredicate:pred];

    [moc lock];
    NSArray *response = [moc executeFetchRequest:fetch error:&error];
    [moc unlock];

    if (error)
    [NSApp presentError:error];

    if ([response count] == 0) // empty resultset --> no artists with this name
    
    [artistObject setValue:theArtist forKey:@"name"];
    NSLog(@"%@ not found. Adding.", theArtist);

    return artistObject;
    
    else
    return [response objectAtIndex:0];


@end

【讨论】:

不行。它仍然点击“返回艺术家对象;”并停止。有趣的是,如果我多次单击触发此操作的按钮,它每次都会对第一首歌曲/艺术​​家重复相同的操作。有没有办法使用调试器找出它在等待什么(如果有的话)? 我不知道,但希望其他人可以加入。【参考方案2】:

你不能锁定 NSManagedObjects!

【讨论】:

以上是关于使用核心数据破坏多线程的主要内容,如果未能解决你的问题,请参考以下文章

设计模式 - 单例模式之多线程调试与破坏单例

核心数据多线程获取记录

多线程违规核心数据

iPhone 核心数据和多线程

关闭 MFC 对话框时的多线程对象破坏

核心数据多线程:代码示例