2010-02-03 17 views
1

我有一个很奇怪的问题,当我根本不明白发生了什么事情时,所以我正在寻找它的解释。情况如下:当数据加载到后台线程时CoreData怪异的行为

我有一个视图控制器scrollview有三个子视图。这三个子视图有方法

-(void)loadContent 

它加载在后台线程使用CoreData数据库的内容,创建代表加载的项目,并将其添加为自己的子视图调用子视图[自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 

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

感谢所有

回答

1

你读过Multi Threading with Core-Data两次?

+0

是的,我读过那个被人掠过的时代。这就是为什么我试图修改我的单例类,使它不是单例,并为每个实例使用单独的托管对象上下文。我不在线程之间发送托管对象实例,我只是根据参数创建图形表示。而我很困惑为什么UIView对象没有被显示! – Matthes

+0

您错过了“对于完全并发操作,您需要为每个线程使用不同的协调器”的部分。然后。你的非单例的行为就像一个单例,因为它具有全局变量来跟踪PSC和MOM的单例实例。由于您使用的是多个线程中的代码,因此可能存在并发问题。初始化它们时肯定会存在潜在的问题,因为您不会同步访问它们。 –

+0

因此,我修改了我的服务类,因此每个实例都保留它自己的协调器实例和托管对象上下文,两个访问器方法都是同步的。但没有改变 - 视图的内容仍然不显示。我不知道如何可能以及从数据库加载数据和显示其他内容之间的关系是什么。哎呀! – Matthes

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 

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

+0

我告诉我改变了它不成为单身人士的方式,所以这正是我想要的 - 每次都有新的实例,每个实例都有单独的托管对象上下文,共享持久性存储协调器和托管对象模型以避免线程同时访问一个托管上下文。这导致我描述的情况 - 不在屏幕上绘制内容,虽然它是创建并附加到视图层次结构。 – Matthes

1

首先,不要在后台线程上加载或构建UI元素。 UI(无论是在桌面上还是在iPhone上)是单线程的,并且在多线程上操作它是一个非常糟糕的主意。其次,您加载到一个上下文中的数据不会在另一个上下文中立即显示。这是什么导致你的问题的一部分。

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