コア データ DB のロック状態 (デバッグ モードで実行すると _objectStoreLockCount カウントが > 1) を修正するのに苦労しています。
以下は、私のアプリケーション (MAC OSX) のアーキテクチャです。
- いくつかのアクティビティを実行するために 2 つの NSOperationQueue を使用しています。各キューには、並行して実行される約 20 のアクティビティがあります。
- これらのアクティビティが完了すると、それぞれが結果データを別の NSOperationQueue に入れます。この NSOperationQueue は、最大同時操作数が 1 に設定されています。
- 次に、結果操作キューはデータをコア データに入れます。
- 他に 2 つのスレッドが実行されています。そのうちの 1 つは、コア データからデータを読み取ることです。もう 1 つは、いくつかのロジックに基づいて、コア データ内の既存のレコードを更新することです。
- コア データ処理のために、3 つのオブジェクトを持つ NSObject のサブクラスを作成しました。各オブジェクトには独自の NSManagedObjectContext があります。データの読み取り用に 1 つ、新しいレコードの書き込み用に 1 つ、既存のレコードの更新用に 1 つです。書き込み管理オブジェクト コンテキストに変更があるたびに、読み取り管理オブジェクト コンテキストを更新しています。3 つの MOC はすべて、共通の永続ストア コーディネーターを持っています。
- アプリケーションは 24 時間年中無休で実行されるはずです 上記のコードはすべて繰り返されます
問題: アプリケーションを実行すると、DB がランダムにハングまたはロックします。任意のランダムな時間でこの状態に達します - 2 時間、定義されていない 10 時間...
アプリケーションのコアデータ処理部分を添付しています。コア データ処理で何か不足している場合はお知らせください。
@implementation MyAppDataRepository
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize writeManagedObjectContext = _writeManagedObjectContext;
@synthesize readManagedObjectContext = _readManagedObjectContext;
@synthesize updateManagedObjectContext = _updateManagedObjectContext;
static MyAppDataRepository *sharedMyAppWriteDataRepository = nil;
static MyAppDataRepository *sharedMyAppReadDataRepository = nil;
static MyAppDataRepository *sharedMyAppupdateDataRepository = nil;
+ (MyAppDataRepository *)sharedMyAppWriteDataRepositoryMyApp
{
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
sharedMyAppWriteDataRepository = [[self alloc] init];
[sharedMyAppWriteDataRepository persistentStoreCoordinator];
[sharedMyAppWriteDataRepository writeManagedObjectContext];
});
return sharedMyAppWriteDataRepository;
}
+ (MyAppDataRepository *)sharedMyAppReadDataRepositoryMyApp
{
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
sharedMyAppReadDataRepository = [[self alloc] init]; // or some other init method
[sharedMyAppReadDataRepository persistentStoreCoordinator];
[sharedMyAppReadDataRepository readManagedObjectContext];
});
return sharedMyAppReadDataRepository;
}
+ (MyAppDataRepository *)sharedMyAppupdateDataRepositoryMyApp
{
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
sharedMyAppupdateDataRepository = [[self alloc] init]; // or some other init method
[sharedMyAppupdateDataRepository persistentStoreCoordinator];
[sharedMyAppupdateDataRepository updateManagedObjectContext];
});
return sharedMyAppupdateDataRepository;
}
-(id)init {
if ((self = [super init])) {
if (!self.writeManagedObjectContext) {
MyAppLOG(@"Core data cannot be initiated");
}
}
return self;
}
// Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.apple.retail.MyApp" in the user's Application Support directory.
- (NSURL *)applicationFilesDirectory
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
return [appSupportURL URLByAppendingPathComponent:@"com.apple.retail.MyApp"];
}
// Creates if necessary and returns the managed object model for the application.
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyApp" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
// Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.)
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
MyAppLOG(@"%@:%@ No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:@[NSURLIsDirectoryKey] error:&error];
if (!properties) {
BOOL ok = NO;
if ([error code] == NSFileReadNoSuchFileError) {
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
} else {
if (![properties[NSURLIsDirectoryKey] boolValue]) {
// Customize and localize this error.
NSString *failureDescription = [NSString stringWithFormat:@"Expected a folder to store application data, found a file (%@).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
//Support for automatic migration of core data
NSMutableDictionary *optionsDictionary = [NSMutableDictionary dictionary];
[optionsDictionary setObject:[NSNumber numberWithBool:YES]
forKey:NSMigratePersistentStoresAutomaticallyOption];
[optionsDictionary setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:@"MyApp.sqlite"];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:url options:optionsDictionary error:&error]) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_persistentStoreCoordinator = coordinator;
return _persistentStoreCoordinator;
}
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
- (NSManagedObjectContext *)writeManagedObjectContext {
if (_writeManagedObjectContext) {
return _writeManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
[dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_writeManagedObjectContext = [[NSManagedObjectContext alloc] init];
[_writeManagedObjectContext setPersistentStoreCoordinator:coordinator];
return _writeManagedObjectContext;
}
- (NSManagedObjectContext *)readManagedObjectContext {
if (_readManagedObjectContext) {
return _readManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
[dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_readManagedObjectContext = [[NSManagedObjectContext alloc] init];
[_readManagedObjectContext setPersistentStoreCoordinator:coordinator];
return _readManagedObjectContext;
}
- (NSManagedObjectContext *)updateManagedObjectContext {
if (_updateManagedObjectContext) {
return _updateManagedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
[dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
NSError *error = [NSError errorWithDomain:@"YOUR_ERROR_DOMAIN" code:9999 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_updateManagedObjectContext = [[NSManagedObjectContext alloc] init];
[_updateManagedObjectContext setPersistentStoreCoordinator:coordinator];
return _updateManagedObjectContext;
}
// Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application.
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
return [[self writeManagedObjectContext] undoManager];
}
// Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user.
- (IBAction)saveAction:(id)sender
{
NSError *error = nil;
if (![[self writeManagedObjectContext] commitEditing]) {
MyAppLOG(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
}
if (![[self writeManagedObjectContext] save:&error]) {
[[NSApplication sharedApplication] presentError:error];
}
}
- (BOOL)saveDataInDatabase:(NSManagedObjectContext *)iContext {
BOOL dataSavedSuccessfully = YES;
NSError *error = nil;
if (![iContext commitEditing]) {
MyAppLOG(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:iContext];
if (![iContext save:&error]) {
dataSavedSuccessfully = NO;
[[NSApplication sharedApplication] presentError:error];
}
// unregister from notification
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSManagedObjectContextDidSaveNotification object:iContext];
return dataSavedSuccessfully;
}
// Merge the changes from write managed object context to read managed object context
- (void)handleDidSaveNotification:(NSNotification *)iNotification {
[[MyAppDataRepository sharedMyAppReadDataRepository].readManagedObjectContext mergeChangesFromContextDidSaveNotification:iNotification];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
// Save changes in the application's managed object context before the application terminates.
if (!_writeManagedObjectContext) {
return NSTerminateNow;
}
if (![[self writeManagedObjectContext] commitEditing]) {
MyAppLOG(@"%@:%@ unable to commit editing to terminate", [self class], NSStringFromSelector(_cmd));
return NSTerminateCancel;
}
if (![[self writeManagedObjectContext] hasChanges]) {
return NSTerminateNow;
}
NSError *error = nil;
if (![[self writeManagedObjectContext] save:&error]) {
// Customize this code block to include application-specific recovery steps.
BOOL result = [sender presentError:error];
if (result) {
return NSTerminateCancel;
}
NSString *question = NSLocalizedString(@"Could not save changes while quitting. Quit anyway?", @"Quit without saves error question message");
NSString *info = NSLocalizedString(@"Quitting now will lose any changes you have made since the last successful save", @"Quit without saves error question info");
NSString *quitButton = NSLocalizedString(@"Quit anyway", @"Quit anyway button title");
NSString *cancelButton = NSLocalizedString(@"Cancel", @"Cancel button title");
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:question];
[alert setInformativeText:info];
[alert addButtonWithTitle:quitButton];
[alert addButtonWithTitle:cancelButton];
NSInteger answer = [alert runModal];
if (answer == NSAlertAlternateReturn) {
return NSTerminateCancel;
}
}
return NSTerminateNow;
}
- (void)insertTestResults:(NSArray *)iTestData withUpdateFlag:(int)iUpdateFlag withDiscardDlag:(int)iDiscardFlag {
NSManagedObjectContext *aWriteManagedObjContext = [[MyAppDataRepository sharedMyAppWriteDataRepositoryMyApp] writeManagedObjectContext];
NSManagedObject *aTestResults;
NSMutableArray *aTestIDArray=[NSMutableArray array];
for (MyAppTestResultsModel *iTestResultsData in iTestData) {
NSString *aTestType=[iTestResultsData valueForKey:kMyAppTestType];
if ([aTestType isEqualToString:kMyAppTest1Type]) {
aTestResults=[NSEntityDescription
insertNewObjectForEntityForName:kMyAppTest1ResultsEntity
inManagedObjectContext:aWriteManagedObjContext];
} else if ([aTestType isEqualToString:kMyAppTest2Type]) {
aTestResults=[NSEntityDescription
insertNewObjectForEntityForName:kMyAppTest2ResultsEntity
inManagedObjectContext:aWriteManagedObjContext];
} else if ([aTestType isEqualToString:kMyAppTest3Type]) {
aTestResults=[NSEntityDescription
insertNewObjectForEntityForName:kMyAppTest3ResultsEntity
inManagedObjectContext:aWriteManagedObjContext];
} else if ([aTestType isEqualToString:kMyAppTest4Type]) {
aTestResults=[NSEntityDescription
insertNewObjectForEntityForName:kMyAppTest4ResultsEntity
inManagedObjectContext:aWriteManagedObjContext];
}
NSError *anError=nil;
if (![self saveDataInDatabase:aWriteManagedObjContext]) {
MyAppLOG(@"Cannot Save!: %@", [anError localizedDescription]);
}
else
{
// Post the saved Message to the main window
NSString *aLog = [NSString stringWithFormat:@"\n \n%@ Test Results Saved \n %@",[iTestResultsData valueForKey:kMyAppTestType], aTestResults];
MyAppLOG(@"%@",aLog);
MyAppINFOLOG(@"Saved test results (in client DB) for Test ID = %@ Test Type = %@ App ID = %@ Network = %@ Status = %@", [iTestResultsData valueForKey:kMyAppTestID], [iTestResultsData valueForKey:kMyAppTestType], [iTestResultsData valueForKey:kMyAppAppIDAttribute], [iTestResultsData valueForKey:kMyAppTestNetwork], [iTestResultsData valueForKey:kMyAppTestStatusAttribute]);
[aTestIDArray addObject:[aTestResults valueForKey:kMyAppTestIDAttribute]];
// Update the isReadyToFlag
if (iUpdateFlag == 1)
[MyAppFetchTestConfigDetails updateReadyToRunForTestID:[NSArray arrayWithArray:aTestIDArray] withInt:iUpdateFlag];
}
}
}
以下は、スレッド 2 (複数のスレッドによって満たされている) の操作キューの実装です。
@implementation MyAppSaveTestResultController
- (id)init {
if ((self = [super init]) != nil) {
self.queue = [[NSOperationQueue alloc] init];
[self.queue setMaxConcurrentOperationCount:1];
}
return self;
}
+ (MyAppSaveTestResultController *)sharedSaveResultsController {
static dispatch_once_t pred = 0;
dispatch_once(&pred, ^{
sharedSaveResultsController = [[self alloc] init]; // or some other init method
});
return sharedSaveResultsController;
}
- (void)startSaveOperation {
MyAppSaveTestResultsOperation *aSaveOperation = [[MyAppSaveTestResultsOperation alloc] initWithTestResults:self.testResults updateFlag:self.updateFlag andDiscardDlag:self.discardFlag];
[self.queue addOperation:aSaveOperation];
}
@implementation MySaveTestResultsOperation
- (id)initWithTestResults:(NSArray *)iTestData updateFlag:(int)iUpdateFlag andDiscardDlag:(int)iDiscardFlag {
if ((self = [super init]) != nil)
{
self.testResults = iTestData;
self.updateFlag = iUpdateFlag;
self.discardFlag = iDiscardFlag;
}
return self;
}
- (void)start {
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
return;
}
// Check for cancellation
if ([self isCancelled]) {
[self completeOperation];
return;
}
// Executing
[self willChangeValueForKey:@"isExecuting"];
executing = YES;
[self didChangeValueForKey:@"isExecuting"];
// Begin
[self beginOperation];
}
- (void)beginOperation {
@try {
[[MyAppDataRepository sharedMyAppWriteDataRepository] insertTestResults:results withUpdateFlag:self.updateFlag withDiscardDlag:self.discardFlag];
[self completeOperation];
} @catch(NSException * e) {
}
}
以下のコードは、MyAppSaveTestResultController にデータを入力するすべてのアクティビティ スレッド (最大 20 の同時スレッド) から呼び出されます。
MyAppSaveTestResultController *aSaveTestResultController = [MyAppSaveTestResultController sharedSaveResultsController];
[aSaveTestResultController saveTestResults:[NSArray arrayWithObject:data] updateFlag:[[NSNumber numberWithBool:[aBlockSelf checkPendingTestsForTestID:anID]] intValue] discardFlag:0];
[aSaveTestResultController startSaveOperation];