2010-02-03 6 views
1

나는 무엇이 계속 진행되고 있는지 이해할 수 없을 때 매우 이상한 문제가 있으므로 설명을 찾고 있습니다. 상황은 다음과 같습니다 :CoreData 데이터가 백그라운드 스레드에로드 될 때

나는 세 개의 하위 뷰가있는 scrollview가있는보기 컨트롤러가 있습니다. 이 세 파단이 백그라운드 스레드에서 CoreData를 사용하여 데이터베이스의 내용을로드하는 방법

-(void)loadContent 

가로드 된 항목을 나타내는 및 호출 자신의 파단으로 추가 파단 생성이 [자기 addSubview를 : itemView] 그 방법은 내가 싱글 서비스 클래스를 사용하고 DB에서 데이터를로드하려면

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

로 호출됩니다. 모든 것이 잘 작동했지만 세 가지보기에서 데이터의 일부가로드되는 경우 앱이 충돌하는 경우가 있습니다.

나는이 모든 읽기 작업에 대해 하나 개의 NSManagedObjectContext 인스턴스를 공유하기 때문에 그것의 추측, 그래서 그것은 단지 NSManagedObjectModel 및 NSPersistentStoreCoordinator 인스턴스 공유하고 자신의 NSManagedObjectContext 인스턴스의 생성하도록 클래스를 다시 썼다.

갑자기 매우 이상한 일이 일어났습니다. 데이터가로드됩니다. 하위 뷰가 생성되어 뷰 계층 구조에 추가되지만 화면에 표시되지 않습니다. 이전 싱글 톤 서비스 클래스 (하나의 managedObjectContext 공유)로 다시 전환하면 매력과 같이 다시 작동합니다! (하지만 앱을 충돌시킬 위험이 있음).

DB에서 데이터를로드하는 것이 화면에 항목을 표시하는 것과 관련이 있다는 사실을 절대 알지 못합니다. 그 이상 - 하위 뷰가 생성되어 뷰 계층에 추가 될 때 왜 그 뷰가 표시되지 않습니까?

소스는 다음과 같습니다

- (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

그래서 서비스 클래스를 수정하여 각 인스턴스가 자신의 코디네이터 인스턴스와 관리 객체 컨텍스트를 유지하고 두 접근 자 메서드가 동기화됩니다. 그러나 변경 사항은 없습니다. 뷰의 내용은 여전히 ​​표시되지 않습니다. 나는 그것이 가능하고 어떻게 DB에서 데이터를 로딩하고 완전히 다른 것을 디스플레이하는지 사이의 관계가 무엇인지 알지 못한다. 아 !!! – Matthes

0

실제로 [WLDataService service]은 싱글 톤을 반환하지 않는다는 것을 알고 있습니까? 매번 새로운 인스턴스를 생성합니다. 따라서 핵심 데이터 구성 요소의 여러 인스턴스로 효과적으로 작업하고 있습니다.

무엇에 대해 : 같은 인스턴스마다 생성됩니다

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 캐시에로드하는 것을 의미합니다. 일단 완료되면 메인 스레드는 메모리에 있기 때문에 데이터를 매우 빠르게 액세스 할 수 있습니다.

관련 문제