ios - NSFetchRequest 挂起
<p><p>我的应用卡在 NSFetchRequest 上时遇到问题。我已经读了好几个小时了。显然存在线程问题,但我不知道如何解决它。</p>
<p>我正在从我的服务器下载一个 JSON 对象,对其进行解析并将其存储在 CoreData 模型中。这一切都发生在我的应用挂起之前完成的后台线程上。这是发生这种情况的代码:</p>
<pre><code>- (void) populateRegistrationList:(NSString *) list script:(NSString *)script
{
NSURL *URL = ];
NSURLRequest *request = ;
NSURLSessionConfiguration *configuration = ;
NSURLSession *session = ;
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request
completionHandler:^(NSURL *localfile, NSURLResponse *response, NSError *error) {
if (!error) {
NSArray *jObj =
options:kNilOptions
error:nil];
// make sure that the result is successful
if (![ isEqualToString:@"success"]) {
return;
}
// get a context pointer to the database model
AppDelegate* appDelegate = .delegate;
self.managedObjectContext = appDelegate.managedObjectContext;
// clear out any data from previous downloads
;
NSArray *jArr = (NSArray *) ;
for (int i=0; i<; i++) {
NSDictionary *dict = ;
;
NSError *error;
if (!)
NSLog(@"Whoops, couldn't save: %@", );
}
// set this user default to YES to tell the RegistrationTwoViewController that we are done
[ setBool:YES forKey:REGISTRATION_LISTS_RETRIEVED];
[ synchronize];
}
}];
;
}
</code></pre>
<p>随后,在主线程中,我执行一个获取请求来访问这些数据,如下所示:</p>
<pre><code>@implementation ChooseSuperGroupViewController
- (void)viewDidLoad
{
;
// get a context pointer to the database model
AppDelegate* appDelegate = .delegate;
self.managedObjectContext = appDelegate.managedObjectContext;
NSLog(@"About to get fetched objects");
[self.managedObjectContext performBlockAndWait:^{
NSFetchRequest *fetchRequest = [ initWithEntityName:@"SuperGroups"];
NSError *error;
self.fetchedObjects = ;
NSLog(@"Done with fetch request");
}];
}
</code></pre>
<p>不幸的是,这卡在 <code>self.fetchedObjects</code> 行。我意识到我正在使用两个不同的线程,但我知道在我尝试获取请求之前后台线程已经完成。为什么我必须在这一点上关注另一个线程?我看不出竞争条件是如何发生的。</p>
<p>我该怎么做才能解决这个问题?我可以强制获取请求以某种方式在同一个后台线程上运行吗?我读过的所有关于这方面的帖子和文章似乎都很复杂,或者根本没有解决线程问题。我被困住了,不知道还能做什么。 </p>
<p>感谢任何帮助。谢谢。</p>
<p>这是来 self 的 AppDelegate:</p>
<pre><code>- (NSManagedObjectContext *) managedObjectContext {
if (_managedObjectContext != nil) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = ;
if (coordinator != nil) {
_managedObjectContext = [ initWithConcurrencyType:NSPrivateQueueConcurrencyType];
;
}
return _managedObjectContext;
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
_managedObjectModel = ;
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator != nil) {
return _persistentStoreCoordinator;
}
NSURL *storeUrl =
stringByAppendingPathComponent: @"PhoneBook.sqlite"]];
NSError *error = nil;
_persistentStoreCoordinator = [
initWithManagedObjectModel:];
if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:nil error:&error]) {
/*Error for store creation should be handled in here*/
}
return _persistentStoreCoordinator;
}
</code></pre></p>
<br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
<p><p>后台线程已经完成并不重要,问题是你不能在不确保同步的情况下安全地在多个线程上使用相同的托管对象上下文——可能通过使用像 <code>dispatch_async< 这样的 GCD 调用</code> 但最好使用 Core Data 的内置同步系统。我不知道为什么在这种情况下它会失败,但你违反了安全使用核心数据的主要规则,所以预期会出现糟糕的结果。</p>
<p>要使用 Core Data 的内置同步,请使用 <code>NSMainQueueConcurrencyType</code> 或 <code>NSPrivateQueueConcurrencyType</code> 创建上下文。然后,每当您使用该上下文(或从该上下文中获取的对象)时,将您的代码放入 <code>performBlock</code> 或 <code>performBlockAndWait</code> 调用中。这将保证同步访问,无论线程或队列的数量如何。</p>
<p>或者,为后台线程创建一个新的托管对象上下文。没有理由不超过一个。在这个单独的上下文上做你的工作,并在主线程上监听 <code>NSManagedObjectContextDidSaveNotification</code> 以合并新的更改。</p>
<p>但无论你做什么,<em>不要</em>在没有足够预防措施的情况下在多个线程上使用相同的上下文。即使它有效,也只是偶然。它最终会失败。</p></p>
<p style="font-size: 20px;">关于ios - NSFetchRequest 挂起,我们在Stack Overflow上找到一个类似的问题:
<a href="https://stackoverflow.com/questions/21595362/" rel="noreferrer noopener nofollow" style="color: red;">
https://stackoverflow.com/questions/21595362/
</a>
</p>
页:
[1]