我发现很难修复核心数据数据库锁定情况(当我在调试模式下运行时,_objectStoreLockCount 计数 > 1)。
下面是我的应用程序(MAC OSX)的架构:
- 我正在使用 2 NSOperationQueue 来执行一些活动。每个队列有大约 20 个并行运行的活动。
- 一旦这些活动完成,它们中的每一个都将结果数据放入另一个 NSOperationQueue 中,该队列的最大并发操作计数设置为 1。
- 然后结果操作队列将数据放入核心数据中。
- 还有 2 个其他线程正在运行。其中之一是从核心数据中读取数据。另一种是基于某些逻辑,更新核心数据中的现有记录。
- 对于核心数据处理,我创建了一个具有 3 个对象的 NSObject 子类。每个对象都有自己的 NSManagedObjectContext - 1 个用于读取数据,1 个用于写入新记录,1 个用于更新现有记录。每当写入托管对象上下文发生变化时,我都会更新读取托管对象上下文。所有三个 MOC 都有共同的持久存储协调器。
- 应用程序应该 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];