다중 스레드 시나리오에서 여러 NSManagedObjectContexts & 시나리오에서 발생하는 것과 동일한 교착 상태 문제가 발생합니다. 일부 뷰 컨트롤러에서 내 앱은 백그라운드 스레드를 사용하여 웹 서비스에서 데이터를 가져오고 동일한 스레드에서 데이터를 저장합니다. 저장하지 않고 더 이상 진행하지 않는 것이 좋은 경우 (예 : 양식이 '다음'에 도달 할 때 양식에서 값을 지속하는 경우) 저장이 주 스레드에서 수행됩니다. AFAIK이 이론이 아무 문제 없을해야하지만, 때때로 나는 교착 상태가CoreData 여러 스레드 스레드 교착 상태
if (![moc save:&error])
에 대한 호출에서 발생 할 수 있습니다 ... 이것은 항상 배경에 수 교착 상태가 발생했을 때의 저장 스레드 것으로 보인다. 그것은 모든 부름에 일어나는 것이 아닙니다. 사실 그것은 꽤 정반대입니다. 몇 분 동안 앱을 사용해야 만합니다.
필자는 Apple 문서 등을 찾을 수있는 모든 게시물을 읽었으며 권장 사항을 따르고 있음을 확신합니다. 구체적으로, 여러 MOC/스레드로 작업하는 것에 대한 나의 이해는 다음과 같습니다.
- 각 스레드마다 자체 MOC가 있어야합니다.
- 스레드의 MOC는 해당 스레드에서 만들어야합니다 (한 스레드에서 다른 스레드로 전달되지 않음).
- NSManagedObject를 전달할 수는 없지만 NSManagedObjectID를 사용하면 ID를 사용하여 다른 MOC를 사용하여 NSManagedObject를 부 풀릴 수 있습니다.
- 둘 다 동일한 PersistentStoreCoordinator를 사용하는 경우 한 MOC에서 변경 한 내용을 다른 항목과 병합해야합니다.
얼마 다시 나는 this SO thread에 MOC 헬퍼 클래스에 대한 몇 가지 코드를 가로 질러 와서 그렇게 내 모든 MOC 상호 작용이 그 통해 지금 사용하기 쉽게 이해할 수있는 아주 편리한 것을 발견했다.
NSManagedObjectID *parentObjectID = [parent objectID];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
// GET BACKGROUND MOC
NSManagedObjectContext *backgroundContext = [ManagedObjectContextHelper managedObjectContext];
Parent *backgroundParent = (Parent*)[backgroundContext objectWithID:parentObjectID];
// HIT THE WEBSERVICE AND PUT THE RESULTS IN THE PARENT OBJECT AND ITS CHILDREN, THEN SAVE...
[ManagedObjectContextHelper commit];
dispatch_sync(dispatch_get_main_queue(), ^{
NSManagedObjectContext *mainManagedObjectContext = [ManagedObjectContextHelper managedObjectContext];
parent = (Parent*)[mainManagedObjectContext objectWithID:parentObjectID];
});
});
오류의 conflictList이가 뭔가 제안하는 것 같다가 교착 것으로 보인다 곳에있다
#import "ManagedObjectContextHelper.h"
@implementation ManagedObjectContextHelper
+(void)initialize {
[[NSNotificationCenter defaultCenter] addObserver:[self class]
selector:@selector(threadExit:)
name:NSThreadWillExitNotification
object:nil];
}
+(void)threadExit:(NSNotification *)aNotification {
TDAppDelegate *delegate = (TDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSString *threadKey = [NSString stringWithFormat:@"%p", [NSThread currentThread]];
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
[managedObjectContexts removeObjectForKey:threadKey];
}
+(NSManagedObjectContext *)managedObjectContext {
TDAppDelegate *delegate = (TDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
[moc setMergePolicy:NSErrorMergePolicy];
return moc;
}
// a key to cache the context for the given thread
NSString *threadKey = [NSString stringWithFormat:@"%p", thread];
// delegate.managedObjectContexts is a mutable dictionary in the app delegate
NSMutableDictionary *managedObjectContexts = delegate.managedObjectContexts;
if ([managedObjectContexts objectForKey:threadKey] == nil) {
// create a context for this thread
NSManagedObjectContext *threadContext = [[NSManagedObjectContext alloc] init];
[threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
[threadContext setMergePolicy:NSErrorMergePolicy];
// cache the context for this thread
NSLog(@"Adding a new thread:%@", threadKey);
[managedObjectContexts setObject:threadContext forKey:threadKey];
}
return [managedObjectContexts objectForKey:threadKey];
}
+(void)commit {
// get the moc for this thread
NSManagedObjectContext *moc = [self managedObjectContext];
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread] == NO) {
// only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:[self class] selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
NSError *error;
if (![moc save:&error]) {
NSLog(@"Failure is happening on %@ thread",[thread isMainThread][email protected]"main":@"other");
NSArray* detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError* detailedError in detailedErrors) {
NSLog(@" DetailedError: %@", [detailedError userInfo]);
}
}
NSLog(@" %@", [error userInfo]);
}
if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:[self class] name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}
+(void)contextDidSave:(NSNotification*)saveNotification {
TDAppDelegate *delegate = (TDAppDelegate *)[[UIApplication sharedApplication] delegate];
NSManagedObjectContext *moc = delegate.managedObjectContext;
[moc performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:saveNotification
waitUntilDone:NO];
}
@end
멀티 스레드 비트의 조각이다 : 여기에 전체 내 ManagedObjectContextHelper 클래스입니다 부모 개체의 OBJECTID과는 : 나는 refreshObject에 넣어 시도했습니다
conflictList = (
"NSMergeConflict (0x856b130) for NSManagedObject (0x93a60e0) with objectID '0xb07a6c0 <x-coredata://B7371EA1-2532-4D2B-8F3A-E09B56CC04F3/Child/p4>'
with oldVersion = 21 and newVersion = 22
and old object snapshot = {\n parent = \"0xb192280 <x-coredata://B7371EA1-2532-4D2B-8F3A-E09B56CC04F3/Parent/p3>\";\n name = \"New Child\";\n returnedChildId = 337046373;\n time = 38;\n}
and new cached row = {\n parent = \"0x856b000 <x-coredata://B7371EA1-2532-4D2B-8F3A-E09B56CC04F3/Parent/p3>\";\n name = \"New Child\";\n returnedChildId = 337046373;\n time = 38;\n}"
);
이론 인 일에, 최대한 빨리이 MOC 잡아 왔으로 호출 에서 이전에 사용한 MOC 인 경우 (예 : 이전에 주 스레드에서 MOC를 사용했고 이것이 도우미 클래스가 제공하는 것과 동일한 것일 수 있습니다.) 그러면 다른 스레드의 저장은 명시 적으로 새로 고쳐야 함을 의미합니다. 하지만 아무런 차이가 없었습니다. 계속 길게 누르면 계속해서 교착 상태가됩니다.
누구에게 아이디어가 있습니까?
편집 : 모든 예외에 브레이크 포인트가 설정된 경우 디버거는 if (![moc save:&error])
행에서 자동으로 일시 중지되므로 재생/일시 중지 버튼이 이미 일시 중지되어 재생 삼각형을 표시하고 있습니다. 모든 예외에 대한 중단 점을 사용하지 않으면 병합 정책이 현재 NSErrorMergePolicy로 설정되어 있기 때문에 실제로 충돌을 기록하고 계속됩니다. 따라서 스레드에서 실제로 교착 상태에 있다고 생각하지 않습니다. 일시 중지 된 상태에서 두 스레드의 상태가 Here's a screehshot입니다.
앱이 교착 상태가되면 어떤 방법으로 메인 스레드에 멈 춥니 까? – lassej
@lassej 다음은 Thread1의 출력입니다 : libsystem_kernel.dylib'mach_msg_trap : 0x98030c18 : movl $ 4294967265, % eax 0x98030c1d : calll 0x9803449a; _sysenter_trap 0x98030c22 : ret 0x98030c23 : nop – bobsmells
나는 그것이 당신이 필요로하는 것이 확실하지 않습니다. Thread6은 [NSManagedObjectContext save]에 있습니다. Thread1이 실제로 차단되었는지는 잘 모르겠지만 제대로 이해한다면 두 스레드의 MOC에서 교착 상태가됩니다. – bobsmells