長話短說,我厭倦了與NSManagedObjectContext
相關的荒謬併發規則(或者說,它完全缺乏對併發性的支持,傾向於爆炸或者如果嘗試分享時做其他不正確的事情跨線程的NSManagedObjectContext
),並試圖實現線程安全變體。使核心數據線程安全
基本上我所做的就是建立追蹤,它創建的線程,然後所有的方法調用映射回該線程的子類。這樣做的機制是稍微令人費解,但它的關鍵是,我有一些輔助的方法,如:
- (NSInvocation*) invocationWithSelector:(SEL)selector {
//creates an NSInvocation for the given selector
NSMethodSignature* sig = [self methodSignatureForSelector:selector];
NSInvocation* call = [NSInvocation invocationWithMethodSignature:sig];
[call retainArguments];
call.target = self;
call.selector = selector;
return call;
}
- (void) runInvocationOnContextThread:(NSInvocation*)invocation {
//performs an NSInvocation on the thread associated with this context
NSThread* currentThread = [NSThread currentThread];
if (currentThread != myThread) {
//call over to the correct thread
[self performSelector:@selector(runInvocationOnContextThread:) onThread:myThread withObject:invocation waitUntilDone:YES];
}
else {
//we're okay to invoke the target now
[invocation invoke];
}
}
- (id) runInvocationReturningObject:(NSInvocation*) call {
//returns object types only
[self runInvocationOnContextThread:call];
//now grab the return value
__unsafe_unretained id result = nil;
[call getReturnValue:&result];
return result;
}
...然後子類以下類似的模式實現了NSManagedContext
接口:
- (NSArray*) executeFetchRequest:(NSFetchRequest *)request error:(NSError *__autoreleasing *)error {
//if we're on the context thread, we can directly call the superclass
if ([NSThread currentThread] == myThread) {
return [super executeFetchRequest:request error:error];
}
//if we get here, we need to remap the invocation back to the context thread
@synchronized(self) {
//execute the call on the correct thread for this context
NSInvocation* call = [self invocationWithSelector:@selector(executeFetchRequest:error:) andArg:request];
[call setArgument:&error atIndex:3];
return [self runInvocationReturningObject:call];
}
}
...然後我有一些代碼,就像去測試它:
- (void) testContext:(NSManagedObjectContext*) context {
while (true) {
if (arc4random() % 2 == 0) {
//insert
MyEntity* obj = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:context];
obj.someNumber = [NSNumber numberWithDouble:1.0];
obj.anotherNumber = [NSNumber numberWithDouble:1.0];
obj.aString = [NSString stringWithFormat:@"%d", arc4random()];
[context refreshObject:obj mergeChanges:YES];
[context save:nil];
}
else {
//delete
NSArray* others = [context fetchObjectsForEntityName:@"MyEntity"];
if ([others lastObject]) {
MyEntity* target = [others lastObject];
[context deleteObject:target];
[context save:nil];
}
}
[NSThread sleepForTimeInterval:0.1];
}
}
所以基本上,我旋轉起來一些線程針對上面的切入點,他們跑了domly創建和刪除實體。這幾乎按照它應該的方式工作。
的問題是,調用obj.<field> = <value>;
時,每隔一段時間一個線程會得到一個EXC_BAD_ACCESS
。我不清楚問題是什麼,因爲如果我在調試器中打印obj
,一切看起來都不錯。有什麼問題可能會有什麼建議(,除了Apple建議不要繼承NSManagedObjectContext的事實)以及如何解決這個問題?
P.S.我知道GCD和NSOperationQueue
和其他技術通常用於「解決」這個問題。這些都不是我想要的。我正在尋找的是NSManagedObjectContext
,它可以自由,安全且直接地被任意數量的線程使用,以查看和更改應用程序狀態,而無需任何外部同步。
問題是您在不同於上下文的線程上操作屬性,因此可能與該上下文中的其他操作(包括保存和刪除)同時操作?您可以嘗試重寫setSomeNumber,setAnotherNumber,setAString以在上下文線程上運行,並查看是否會影響結果。 – paulmelnikow
是的,似乎已經穩定它。所以現在的問題是,我該如何創建一個動態注入線程安全屬性設置器實現的NSManagedObject子類? – aroth
我得到了setter注入的東西工作。它比'NSManagedObjectContext'更改更令人費解。但重要的是它的工作原理。如果有人有興趣,我會分享相關部分的代碼。 – aroth