English 中文(简体)
当数据在后台线程加载时,CoreData的行为变得奇怪。
原标题:CoreData weird behavior when data are loaded on background thread

我有很奇怪的问题,因为我根本不理解什么进展,所以我想解释一下。 情况如下:

我有一个带有UIScrollView和三个子视图的视图控制器。这三个子视图有方法。

-(void)loadContent

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

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

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

我猜测这是因为它共享了一个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

我非常想知道这里发生了什么,为什么它表现得那么奇怪(当然,特别是为什么它不起作用)。有人能够解释一下吗?

感谢所有人

问题回答

你已经读过使用Core-Data进行多线程编程两遍了吗?

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

第二,在另一个背景下,你所装载的数据不会立即可见。 这是造成你问题的一部分。

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

您意识到[WLDataService服务]实际上并不返回一个单例吗?它每次都会创建一个新实例。因此,您实际上正在使用多个Core Data组件实例。

怎么样? (Zěnme yàng?)

static WLDataService* gSharedService = NULL;

@implementation WLDataService

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

@end

每次都会创建相同的实例。您还需要使用 @synchronized 块使您的 managedObjectContextmanagedObjectModelpersistentStoreCoordinator 方法线程安全。否则,可能会出现多个线程同时初始化这些方法的情况,导致意外行为。





相关问题
Silverlight, Updating the UI during processing

I have a simple silverlight multifile upload application, and i want to provide the user with some feedback, right now its only in a test phase and i dont have the webservice. Somehow i cant get the ...

Is reading from an XmlDocument object thread safe?

I was wondering if i could safely read from an XmlDocument object using SelectNodes() and SelectSingleNode() from multiple threads with no problems. MSDN says that they are not guaranteed to be ...

Terminating a thread gracefully not using TerminateThread()

My application creates a thread and that runs in the background all the time. I can only terminate the thread manually, not from within the thread callback function. At the moment I am using ...

热门标签