1

我发现很难修复核心数据数据库锁定情况(当我在调试模式下运行时,_objectStoreLockCount 计数 > 1)。

下面是我的应用程序(MAC OSX)的架构:

  1. 我正在使用 2 NSOperationQueue 来执行一些活动。每个队列有大约 20 个并行运行的活动。
  2. 一旦这些活动完成,它们中的每一个都将结果数据放入另一个 NSOperationQueue 中,该队列的最大并发操作计数设置为 1。
  3. 然后结果操作队列将数据放入核心数据中。
  4. 还有 2 个其他线程正在运行。其中之一是从核心数据中读取数据。另一种是基于某些逻辑,更新核心数据中的现有记录。
  5. 对于核心数据处理,我创建了一个具有 3 个对象的 NSObject 子类。每个对象都有自己的 NSManagedObjectContext - 1 个用于读取数据,1 个用于写入新记录,1 个用于更新现有记录。每当写入托管对象上下文发生变化时,我都会更新读取托管对象上下文。所有三个 MOC 都有共同的持久存储协调器。
  6. 应用程序应该 24*7 运行 上面的所有代码都必须重复

问题:当我运行我的应用程序时,它会随机挂起或锁定数据库。它在任何随机时间达到此状态 - 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