在后台线程上加载数据时CoreData奇怪的行为

时间:2010-02-03 14:30:36

标签: iphone multithreading core-data

我有一个非常奇怪的问题,当我不明白发生了什么事时,所以我正在寻找它的解释。情况如下:

我有一个带有scrollview的视图控制器,里面有三个子视图。这三个子视图有方法

-(void)loadContent

在后台线程中使用CoreData从数据库加载内容,创建表示已加载项的子视图,并将它们添加为自己的子视图,调用[self addSubview:itemView];该方法被调用为

[self performSelectorInBackground: @selector(loadContent) withObject: nil];

从DB加载数据我正在使用单例服务类。一切正常,但当这三个视图加载他们的数据部分时,它有时会崩溃应用程序。

我猜这是因为它为所有读取操作共享一个NSManagedObjectContext实例,所以我重新编写了类,因此它只共享NSManagedObjectModel和NSPersistentStoreCoordinator实例并创建它自己的NSManagedObjectContext实例。

突然,发生了一件非常奇怪的事情。数据加载正常,子视图被创建并添加到视图层次结构中,但它从未在屏幕上显示。当我切换回旧的单件服务类(共享一个managedObjectContext)时,它再次像魅力一样工作! (但是有可能会崩溃应用程序)。

我绝对不明白如何从数据库加载数据与在屏幕上显示项目有关。更多相关内容 - 当创建子视图并将其添加到视图层次结构时,为什么它不会显示?

源代码如下:

- (void) loadContent {

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

NSArray *results = [(WLDataService *)[WLDataService service] loadItemsForGDView];

NSUInteger channelPosition = 0;
CGFloat position = 0.0;
CGFloat minuteWidth = ((self.superview.frame.size.width / 2.0) / 60.0);

for(Item *it in results) {


 /// On following lines size and position of the view is computed according to item setup - skipping here...

 /// Create item; it's simple subclass of UIView class
 WLGDItemView *item = [[WLGDItemView alloc] init];

 /// Variables used here are declared above when size and position is computed
 item.frame = CGRectMake(itemX, itemY, itemWidth, itemHeight);

 [self performSelectorOnMainThread: @selector(addSubview:) withObject: item waitUntilDone: NO];

            /// This is just helper macro to release things
             WL_RELEASE_SAFELY(item);
}

[pool drain];
}

基本服务类(非单例)实现如下(只是有趣的部分):

#import "WLLocalService.h"

static NSPersistentStoreCoordinator *sharedPSC = nil;
static NSManagedObjectModel *sharedMOM = nil;

@implementation WLLocalService

@synthesize managedObjectContext;

/// This is here for backward compatibility reasons
+ (WLLocalService *) service {

return [[[self alloc] init] autorelease];

 }

#pragma mark -
#pragma mark Core Data stack

- (NSManagedObjectContext *) managedObjectContext {

if (managedObjectContext == nil) {

 NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];

 if (coordinator != nil) {
  managedObjectContext = [[NSManagedObjectContext alloc] init];
  [managedObjectContext setPersistentStoreCoordinator: coordinator];
 }

 [managedObjectContext setUndoManager: nil];
 [managedObjectContext setMergePolicy: NSMergeByPropertyStoreTrumpMergePolicy];
}

return managedObjectContext;
}

- (NSManagedObjectModel *) managedObjectModel {

if(sharedMOM == nil) {
 sharedMOM = [[NSManagedObjectModel mergedModelFromBundles: nil] retain];
}

return sharedMOM; 
}

- (NSPersistentStoreCoordinator *) persistentStoreCoordinator {

if(sharedPSC == nil) {

 NSURL *storeUrl = [self dataStorePath];

 NSError *error = nil;
 sharedPSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];

 if (![sharedPSC addPersistentStoreWithType: NSSQLiteStoreType configuration: nil URL: storeUrl options: nil error: &error]) {
  WLLOG(@"%@: %@", error, [error userInfo]);
 }
}

return sharedPSC;
}

#pragma mark -
#pragma mark Path to data store file
- (NSURL *) dataStorePath {
return [NSURL fileURLWithPath: [WL_DOCUMENTS_DIR() stringByAppendingPathComponent: @"/DB.sqlite"]];
}

- (void)dealloc {

WL_RELEASE_SAFELY(managedObjectModel);

[super dealloc];
}

@end

我真的很想知道这里发生了什么,为什么它表现得如此奇怪(当然 - 为什么它不起作用,特别是)。任何人都可以解释一下吗?

感谢所有

3 个答案:

答案 0 :(得分:1)

你有两次阅读Multi Threading with Core-Data吗?

答案 1 :(得分:1)

首先,不要在后台线程上加载或构造UI元素。 UI(无论是在桌面上还是在iPhone上)是单线程的,并且在多个线程上操作它是一个非常糟糕的主意。

其次,加载到一个上下文中的数据在另一个上下文中不会立即可见。这是造成部分问题的原因。

解决方案是将所有UI代码移动到主线程,并在后台线程上预热Core Data缓存。这意味着将数据加载到后台线程(进入单独的缓存)以将其加载到NSPersistentStoreCoordinator缓存中。一旦完成,您的主线程可以非常快速地访问该数据,因为它现在在内存中。

答案 2 :(得分:0)

您意识到[WLDataService service]实际上并没有返回单身人士?它每次都会创建一个新实例。因此,您正在有效地处理Core Data组件的多个实例。

怎么样:

static WLDataService* gSharedService = NULL;

@implementation WLDataService

+ (id) service
{
    @synchronized (self) {
        if (gSharedService == NULL) {
            gSharedService = [[self alloc] init];
        }
    }
    return gSharedService;
}

@end

每次都会创建相同的实例。您还希望使用managedObjectContext块使managedObjectModelpersistentStoreCoordinator@synchronized方法线程安全。否则会有一个更改,多个线程会同时初始化这些更改,从而导致意外行为。