我目前正在编写 iOS 应用程序的同步引擎。我正在编写的方法之一是重新加载数据函数,其中应用程序重新下载用户的数据及其所有照片。这是一项昂贵的操作(时间方面),所以我创建了一个NSOperation
子类,SSReloadDataOperation
。它下载数据,获取 currentUser 实体,从该 currentUser 中删除所有现有照片,然后重新填充它。
然而,即使我认为这是线程安全的,有时在操作运行时并且-currentUser
从其他地方访问时,应用程序崩溃,大概是在尝试获取它时。其他时候,UI 有时只是冻结,并且在调试器中暂停显示它总是停在某个位置-currentUser
NSFetchRequest
执行调用。
如何使此操作线程安全且原子,以便我可以下载并重新填充而不阻塞主 UI 线程,并且仍然有-currentUser
可以访问吗?在使用锁或架构方面我缺少什么吗?谢谢你!
Code:
- (void)main
{
// Download the photo data
[[SyncEngine defaultEngine] getUserPhotosWithCompletionBlock:^(NSMutableArray *photos)
{
if (photos)
{
// Create a new NSManagedObjectContext for this operation
SSAppDelegate* appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext* localContext = [[NSManagedObjectContext alloc] init];
[localContext setPersistentStoreCoordinator:[[appDelegate managedObjectContext] persistentStoreCoordinator]];
NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:localContext];
NSError* error;
NSFetchRequest* request = [[[SyncEngine defaultEngine] managedObjectModel] fetchRequestFromTemplateWithName:@"CurrentUser" substitutionVariables:[[NSDictionary alloc] init]];
User* currentUser = [[localContext executeFetchRequest:request error:&error] objectAtIndex:0];
// Remove the old data
[currentUser setPhotos:[[NSSet alloc] init]];
// Iterate through photo data, repopulate
for (Photo* photo in photos) {
[currentUser addPhotosObject:photo];
}
if (! [localContext save:&error]) {
NSLog(@"Error saving: %@", error);
}
NSLog(@"Completed sync!");
}
} userId:[[[SyncEngine defaultEngine] currentUser] userId]];
}
-currentUser 便捷方法,通常从主线程调用。
- (User *)currentUser
{
NSError* error;
NSFetchRequest* request = [self.managedObjectModel fetchRequestFromTemplateWithName:@"CurrentUser" substitutionVariables:[[NSDictionary alloc] init]];
NSArray* result = [self.managedObjectContext executeFetchRequest:request error:&error];
if ([result count] == 1) {
return [result objectAtIndex:0];
}
return nil;
}
你是对的,这种冻结感觉像是线程问题。
因为托管对象必须在使用其上下文和创建它们的同一线程或串行队列中使用,所以不可能有像这样的方法currentUser
从示例中,让它神奇地返回线程安全的托管对象。
但您可以将上下文作为参数传递。调用者将决定在哪种上下文中复活用户。
- (User *)userWithContext:(NSManagedContext *)context {
User *user = nil;
NSError *error;
NSFetchRequest *request = ...;
NSArray *userArray = [context executeFetchRequest:request error:&error];
if (userArray == nil) {
NSLog(@“Error fetching user: %@“, error);
} else if ([userArray count] > 0) {
user = userArray[0];
}
return user;
}
这是关于您的代码片段的其他想法。
您在操作的主上下文中创建的上下文可以是主上下文的子上下文。然后,保存该子项后,保存父项(如果您希望此时将数据存储到存储中)。使用父子关系时,您不需要订阅 did-save 通知并合并其中的更改。
从技术上讲,您无法从操作的 main 访问主上下文的持久存储协调器。因为它只允许与来自一个线程或队列的上下文一起工作(调用任何方法)。我打赌你在主线程上创建了你的主要上下文。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)