核心数据:在多线程 iOS 应用程序中实现多个 NSManagedObjects 和 NSFetchedResultsController 非常困惑
Posted
技术标签:
【中文标题】核心数据:在多线程 iOS 应用程序中实现多个 NSManagedObjects 和 NSFetchedResultsController 非常困惑【英文标题】:Core-Data: Very confused by the implementation of multiple NSManagedObjects and an NSFetchedResultsController in a multithreading iOS app 【发布时间】:2013-10-08 18:59:38 【问题描述】:我有一个例程在后台获取 RSS 条目,如果还没有的话,将它们插入到我的 NSManagedObjectContext 中。
我的问题是这个对象找不到重复或崩溃,这取决于NSManagedObjectContext I use
...请帮助我。
这里是简化的.h
@interface AsyncFetchEngine : NSObject <NSXMLParserDelegate,NSFetchedResultsControllerDelegate>
@property (strong, nonatomic) dispatch_queue_t rssParserQueue;
@property (strong, nonatomic) NSMutableArray *uRLsToFetch;
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSManagedObjectContext *childManagedObjectContext;
@property (strong, nonatomic) NSFetchedResultsController *fetchedResultsController;
- (Boolean) isAlreadyInTheFetchQueue:(Feed *)feed;
- (void) fetchPosts:(Feed *)feed;
- (void) createPostInFeed:(Feed*)feed withTitle:(NSString *)title withContent:(NSString *)content withURL:(NSString *)url withDate:(NSDate *)date;
现在是 init 方法: 请注意,如果我在这里设置 _childManagedObjectContext 的父级,程序会崩溃。
-(AsyncFetchEngine *)init
_rssParserQueue = dispatch_queue_create("com.example.MyQueue", NULL);
_uRLsToFetch = [[NSMutableArray alloc] initWithCapacity:32];
_childManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[_childManagedObjectContext setPersistentStoreCoordinator:[_managedObjectContext persistentStoreCoordinator]];
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:_childManagedObjectContext];
return self;
- (void)contextDidSave:(NSNotification*)notification
void (^mergeChanges) (void) = ^
[_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
;
if ([NSThread isMainThread])
mergeChanges();
else
dispatch_sync(dispatch_get_main_queue(), mergeChanges);
获取方法: 注意:不确定使用哪个 MOC 来确定 localFeed,_managedObjectContext 会使应用程序崩溃。
- (void) fetchPosts:(Feed *)feed
if (!_childManagedObjectContext.parentContext)
[_childManagedObjectContext setParentContext:self.managedObjectContext];
if ([self isAlreadyInTheFetchQueue:feed])
NSLog(@"AsyncFetchEngine::fetchPosts> \"%@\" is already in the fetch queue", feed.name);
return;
[_uRLsToFetch addObject:feed];
NSURL *url=[NSURL URLWithString:feed.rss];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:url];
if (![NSURLConnection canHandleRequest:req])
return;
Feed *localFeed = ((Feed *)[_childManagedObjectContext existingObjectWithID:[feed objectID] error:nil]);
dispatch_async(_rssParserQueue, ^
NSLog(@"AsyncFetchEngine::fetchPosts> Opening %@", feed.rss);
[RSSParser parseRSSFeedForRequest:req success:^(NSArray *feedItems)
for(RSSItem *i in feedItems)
[self createPostInFeed:localFeed withTitle:i.title withContent:(i.content?i.content:i.itemDescription) withURL:[i.link absoluteString] withDate:(i.pubDate?i.pubDate:[NSDate date])];
NSLog(@"AsyncFetchEngine::fetchPosts> Found %d items", [feedItems count]);
[_uRLsToFetch removeObject:feed];
failure:^(NSError *error)
NSLog(@"AsyncFetchEngine::fetchPosts> RSSParser lost it: %@", [error localizedDescription]);
];
);
- (Boolean) isAlreadyInTheFetchQueue:(Feed *)feed
Feed *f=nil;
for (f in _uRLsToFetch)
if ([f isEqual:feed])
return YES;
return NO;
NSFecthedResultsController,我需要这样吗?
- (NSFetchedResultsController *)fetchedResultsController
if (_fetchedResultsController != nil)
return _fetchedResultsController;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
// Note: I originally tried to use the main MOC here, but it also used to crash the app.
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Post" inManagedObjectContext:_childManagedObjectContext];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key as appropriate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
NSArray *sortDescriptors = @[sortDescriptor];
[fetchRequest setSortDescriptors:sortDescriptors];
/* NSPredicate *predicate =[NSPredicate predicateWithFormat:@"feed.rss LIKE '%@'", _detailItem.rss];
[fetchRequest setPredicate:predicate]; */
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:nil];
aFetchedResultsController.delegate = self;
_fetchedResultsController = aFetchedResultsController;
return _fetchedResultsController;
根据转储堆栈,它通常在此方法的某个地方崩溃。 如果它没有崩溃,我会将我的帖子添加到(空)类别...
- (void)createPostInFeed:(Feed*)feed withTitle:(NSString *)title withContent:(NSString *)content withURL:(NSString *)url withDate:(NSDate *)date
[_childManagedObjectContext performBlockAndWait:^
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error])
NSLog(@"Error in refetch: %@",[error localizedDescription]);
abort();
];
NSLog(@"AsyncFetchEngine.h: Searching similar posts among %d", [[self.fetchedResultsController fetchedObjects] count]);
Boolean found=NO;
NSPredicate *predicate=[NSPredicate predicateWithFormat:@"title == %@ AND url == %@ AND feed == %@", title, url, feed];
NSArray *similarPosts = [_fetchedResultsController.fetchedObjects filteredArrayUsingPredicate:predicate];
if ([similarPosts count] > 0)
NSLog(@"\n\n\n\t\tAsyncFetchEngine::fetchPosts> Skipping %@ (%@)", title, url);
else
NSLog(@"\n\n\n\t\tAsyncFetchEngine::fetchPosts> Putting new post in %@", feed.name);
NSEntityDescription *postEntityDescription = [NSEntityDescription entityForName:@"Post"
inManagedObjectContext:_childManagedObjectContext];
[_childManagedObjectContext performBlock:^
Post *initPost = (Post *)[[NSManagedObject alloc]
initWithEntity:postEntityDescription
insertIntoManagedObjectContext:_childManagedObjectContext];
initPost.title = title;
initPost.url = url;
initPost.excerpt = content;
initPost.date = date;
initPost.read = nil;
initPost.feed = feed;
NSError *error;
if (![_childManagedObjectContext save:&error])
NSLog(@"[createPost] Error saving context: %@", error);
NSLog(@"Created: %@ (%@)", title, url);
//[[NSNotificationCenter defaultCenter] postNotificationName:@"NewPostAdded" object:self];
];
所以,我的问题是:
当我有多个 MOC 时,我是否从 main 中获取并写入一个 child? 我应该如何分别使用以上这些?谢谢!
【问题讨论】:
【参考方案1】:为了最终帮助另一个新手而回答我自己的问题。
使用 Coredata 检查 NSOperationQueue。 有一个很好的例子here。
【讨论】:
以上是关于核心数据:在多线程 iOS 应用程序中实现多个 NSManagedObjects 和 NSFetchedResultsController 非常困惑的主要内容,如果未能解决你的问题,请参考以下文章