1

コア データ DB のロック状態 (デバッグ モードで実行すると _objectStoreLockCount カウントが > 1) を修正するのに苦労しています。

以下は、私のアプリケーション (MAC OSX) のアーキテクチャです。

  1. いくつかのアクティビティを実行するために 2 つの NSOperationQueue を使用しています。各キューには、並行して実行される約 20 のアクティビティがあります。
  2. これらのアクティビティが完了すると、それぞれが結果データを別の NSOperationQueue に入れます。この NSOperationQueue は、最大同時操作数が 1 に設定されています。
  3. 次に、結果操作キューはデータをコア データに入れます。
  4. 他に 2 つのスレッドが実行されています。そのうちの 1 つは、コア データからデータを読み取ることです。もう 1 つは、いくつかのロジックに基づいて、コア データ内の既存のレコードを更新することです。
  5. コア データ処理のために、3 つのオブジェクトを持つ NSObject のサブクラスを作成しました。各オブジェクトには独自の NSManagedObjectContext があります。データの読み取り用に 1 つ、新しいレコードの書き込み用に 1 つ、既存のレコードの更新用に 1 つです。書き込み管理オブジェクト コンテキストに変更があるたびに、読み取り管理オブジェクト コンテキストを更新しています。3 つの MOC はすべて、共通の永続ストア コーディネーターを持っています。
  6. アプリケーションは 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];
4

0 に答える 0