我正在尝试一个基于聊天的应用程序。我已经设置了一个单例核心数据管理器,如下所示
#import "CGSharedCoreData.h"
CGSharedCoreData *_cd;
@implementation CGSharedCoreData
@synthesize managedObjectModel = managedObjectModel_;
@synthesize managedObjectContext = managedObjectContext_;
@synthesize persistentStoreCoordinator = persistentStoreCoordinator_;
+ (CGSharedCoreData *)sharedCoreData{
static CGSharedCoreData *_cd = nil;
static dispatch_once_t onceCoreDataShared;
dispatch_once(&onceCoreDataShared, ^{
_cd = [[CGSharedCoreData alloc] init];
});
return _cd;
}
+ (void)saveContext:(NSManagedObjectContext*)c{
@try {
if (c.persistentStoreCoordinator.persistentStores.count == 0)
{
// This is the case where the persistent store is cleared during a logout.
CGLog(@"saveContext: PersistentStoreCoordinator is deallocated.");
return;
}
// Register context with the notification center
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
CGSharedCoreData *sharedCoreData = [CGSharedCoreData sharedCoreData];
[nc addObserver:sharedCoreData
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:c];
NSError *error = nil;
if (c != nil) {
CGLog(@"thread &&*&(*(* %d",[NSThread isMainThread]);
if ([c hasChanges] && ![c save:&error]) {
CGLog(@"Unresolved error %@, %@", error, [error userInfo]);
}
}
[nc removeObserver:sharedCoreData name:NSManagedObjectContextDidSaveNotification
object:c];
}
@catch (NSException *exception) {
CGLog(@"***** Unresolved CoreData exception %@", [exception description]);
}
}
+ (dispatch_queue_t) backgroundSaveQueue
{
static dispatch_queue_t coredata_background_save_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
coredata_background_save_queue = dispatch_queue_create("com.shashank.coredata.backgroundsaves", NULL);
});
return coredata_background_save_queue;
}
+ (void)performInTheBackground:(void (^)(NSManagedObjectContext *blockContext))bgBlock {
dispatch_async([CGSharedCoreData backgroundSaveQueue],
^{
NSManagedObjectContext *newContext = [[NSManagedObjectContext alloc] init];
[newContext setPersistentStoreCoordinator:CG_CORE_DATA.persistentStoreCoordinator]; // Create a managed object context
[newContext setStalenessInterval:0.0];
[newContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
bgBlock(newContext);
});
}
- (void)saveContext {
[CGSharedCoreData saveContext:managedObjectContext_];
}
- (void)clearStore {
NSError *error = nil;
if (self.persistentStoreCoordinator) {
if ([persistentStoreCoordinator_ persistentStores] == nil) {
CGLog(@"No persistent stores to clear!");
}
else {
CGLog(@"Cleaning persistent stores!");
managedObjectContext_ = nil;
NSPersistentStore *store = [[persistentStoreCoordinator_ persistentStores] lastObject];
if (![persistentStoreCoordinator_ removePersistentStore:store error:&error]) {
CGLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
// Delete file
if ([[NSFileManager defaultManager] fileExistsAtPath:store.URL.path]) {
if (![[NSFileManager defaultManager] removeItemAtPath:store.URL.path error:&error]) {
CGLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
}
// Delete the reference to non-existing store
persistentStoreCoordinator_ = nil;
}
}
}
#pragma mark - Core Data stack
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
- (NSManagedObjectContext *)managedObjectContext
{
NSAssert([NSThread isMainThread], @"Must be instantiated on main thread.");
if (managedObjectContext_ != nil) {
return managedObjectContext_;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (coordinator != nil)
{
managedObjectContext_ = [[NSManagedObjectContext alloc] init];
[managedObjectContext_ setPersistentStoreCoordinator:coordinator];
[managedObjectContext_ setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
}
return managedObjectContext_;
}
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
- (NSManagedObjectModel *)managedObjectModel
{
if (managedObjectModel_ != nil) {
return managedObjectModel_;
}
managedObjectModel_ = [NSManagedObjectModel mergedModelFromBundles:nil];
return managedObjectModel_;
}
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}
CGLog(@"Creating a persistent store!");
NSURL *storeURL = [NSURL fileURLWithPath: [[self applicationLibraryDirectory] stringByAppendingPathComponent: @"CGChatData.sqlite"]];
NSError *error = nil;
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
CGLog(@"Unresolved error %@, %@", error, [error userInfo]);
// abort();
CGLog(@"Delete STORE: %d",[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]);
persistentStoreCoordinator_ = nil;
persistentStoreCoordinator_ = self.persistentStoreCoordinator;
}
return persistentStoreCoordinator_;
}
#pragma mark -
#pragma mark Handling Multiple Contexts
/**
Merges the changes from the insert contexts to the main context on the main thread.
*/
- (void)mergeChanges:(NSNotification *)notification
{
// Merge changes into the main context on the main thread
if(![[CGLoginEngine sharedLoginEngine] isLoggedIn])
return;
if (![NSThread isMainThread])
{
[self performSelectorOnMainThread:@selector(mergeChanges:)
withObject:notification waitUntilDone:YES];
return;
}
//CAUTION: Without the for clause below the NSFetchedResultsController may not capture all the changed objects.
//For more info see: http://stackoverflow.com/questions/3923826/nsfetchedresultscontroller-with-predicate-ignores-changes-merged-from-different
//and http://stackoverflow.com/questions/2590190/appending-data-to-nsfetchedresultscontroller-during-find-or-create-loop
for (NSManagedObject *object in [[notification userInfo] objectForKey:NSUpdatedObjectsKey])
{
[[managedObjectContext_ objectWithID:[object objectID]] willAccessValueForKey:nil];
}
[managedObjectContext_ mergeChangesFromContextDidSaveNotification:notification];
}
现在,每当我需要发送消息时,我都会将其写入核心数据并使用获取的结果控制器更新表格视图。写作部分如下所示:
[CGSharedCoreData performInTheBackground:^(NSManagedObjectContext *blockContext)
{
CGLog(@"thread ******* is main %d",[NSThread isMainThread]);
[[CGChatsModelController sharedChatModel] addChatWithtext:[chatMessage objectForKey:@"Message"]
username:[chatMessage objectForKey:@"Receiver"]
firstName:[chatMessage objectForKey:@"ReceiverFirstName"]
lastName:[chatMessage objectForKey:@"ReceiverLastName"]
imageId:[chatMessage objectForKey:@"ReceiverImageID"]
createdByMe:YES
time:time
context:blockContext];
[CGSharedCoreData saveContext:blockContext];
但是,当我在很短的时间内发送多条消息时,它会完全阻塞我的 UI,即使核心数据保存和所有其他相关操作正在后台队列中完成。发生这种情况有什么特别的原因吗?
我在这里附上我的代码的其他几个块供参考:
- (CGChat *) addChatWithtext:(NSString *)text username:(NSString *)username firstName:(NSString *)firstName lastName:(NSString *)lastName imageId:(NSString *)imageId createdByMe:(BOOL)yesOrNo time:(NSDate *)date context:(NSManagedObjectContext *)context
{
NSManagedObjectContext *backgroundContext = context;
CGChat *chat = (CGChat *)[NSEntityDescription insertNewObjectForEntityForName:@"CGChat" inManagedObjectContext:backgroundContext];
chat.text = text;
chat.createdByMe = [NSNumber numberWithBool:yesOrNo];
chat.status = @"sent";
[self addChat:chat toUserWithUserName:username firstName:firstName lastName:lastName imageID:imageId time:date WithContext:backgroundContext];
return chat;
}
- (CGChat *)lookUpChatWithUserName:(NSString *)username text:(NSString *)text timeStamp:(NSString *)timeStamp createdByMe:(BOOL) yesOrNo context:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"CGChat" inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"userIchatWith == %@ && text == %@ && timeStamp == %@ && createdByMe ==%@", [self lookUpForUserWithUsername:username inContext:context],text,timeStamp,[NSNumber numberWithBool:yesOrNo]];
[request setPredicate:predicate];
NSArray *resultArray = nil;
NSError *error;
resultArray = [context executeFetchRequest:request error:&error];
CGChat *chat = nil;
if ([resultArray count] > 0) {
chat = [resultArray objectAtIndex:0];
}
return chat;
}
- (void) addChat:(CGChat *)chat toUserWithUserName:(NSString *)username firstName:(NSString *)firstName lastName:(NSString *)lastName imageID:(NSString *)imageId time:(NSDate *)date WithContext:(NSManagedObjectContext *)context
{
CGUser *user = [self lookUpForUserWithUsername:username inContext:context];
if (!user)
{
user = (CGUser *)[NSEntityDescription insertNewObjectForEntityForName:@"CGUser" inManagedObjectContext:context];
user.userName = username ;
}
user.firstName = firstName;
user.lastName = lastName;
if (![user.imageID isEqualToString:imageId])
{
user.imageID = imageId;
}
CGChat *chats = [self getLastChatForUsername:username andContext:context];
if(chats)
{
chats.isLastChat = [NSNumber numberWithBool:NO];
}
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
chat.timeStamp = [dateFormat stringFromDate:date];
chat.isLastChat = [NSNumber numberWithBool:YES];
chat.userIchatWith = user;
user.timeOfLatestChat = [dateFormat stringFromDate:date];
}
- (CGChat *) getLastChatForUsername:(NSString *)username andContext:(NSManagedObjectContext *)context
{
CGUser *user = [self lookUpForUserWithUsername:username inContext:context];
if(user)
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"isLastChat == %@",[NSNumber numberWithBool:YES]];
NSSet *filteredSet = [user.chats filteredSetUsingPredicate:predicate];
return [[filteredSet allObjects] lastObject];
}
else
{
return nil;
}
}
- (CGUser *) lookUpForUserWithUsername:(NSString *)username inContext:(NSManagedObjectContext *)context
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"CGUser" inManagedObjectContext:context];
[request setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"userName == %@", username];
[request setPredicate:predicate];
NSArray *resultArray = nil;
NSError *error;
resultArray = [context executeFetchRequest:request error:&error];
CGUser *user = nil;
if ([resultArray count] > 0) {
user = [resultArray objectAtIndex:0];
}
return user;
}