iOS中的NSOperation – 如何处理循环和嵌套的NSOperation调用来获取图像
我需要帮助了解如何处理以下用例:
说我正在写一个聊天应用程序:
- 启动应用程序
- 询问服务器(
AFHTTPRequestOperation
)给我一个所有新消息的列表 - 通过这些消息循环,看看我是否需要下载任何图像
- 如果是的话,再次调用服务器(
AFImageRequestOperation
)来获取图像
我不断收到我的托pipe对象“消息”不再在同一个上下文中的崩溃,但我只使用一个managedObjectContext
。
是因为我嵌套的调用方式,因为它们是asynchronous的? 我几乎积极的消息不会被删除在其他地方,因为我看到它。
有一点要注意的是,当我改变视图控制器似乎发生。 我启动应用程序,并在根视图控制器(RVC)上执行上面的步骤#2。 如果在下载所有图像之前触摸了“ MessageListViewController
”(MLVC),则那些没有完成下载的图像的关联消息突然没有managedObjectContext
。
以下是相关的代码:
AFHTTPRequestOperation * operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:requestImageInfoURL success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { NSDictionary * JSONresponse = (NSDictionary *)JSON; if( [[[JSONresponse objectForKey:@"status"] uppercaseString] isEqualToString:@"ERROR"] ) { for( id<CommsObserver> observer in _observers ) [observer errorOccurred:[JSONresponse objectForKey:@"message"]]; } else { if( [JSONresponse containsKey:@"convoMessages"] ) { NSArray * messageList = [JSON objectForKey:@"messages"]; for( int i = 0 ; i < messageList.count ; i++ ) { __block ConversationMessage * message = [JSONUtility convoMessageFromJSON:[messageList objectAtIndex:i]]; if( !message ) NSLog( @"Couldn't create the new message..." ); else { message.unread = [NSNumber numberWithBool:YES]; [[DataController sharedController] saveContext]; if( (!message.text || [message.text isEqualToString:@""]) && (message.image || message.imageInfoKey) ) { NSString * imageURL = (message.image ? [Comms urlStringForImageInfo:message.image] : [Comms urlStringForImageKey:message.imageInfoKey extension:message.imageInfoExt]); NSURLRequest *requestImageURL = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]]; AFImageRequestOperation * imageOperation; imageOperation = [AFImageRequestOperation imageRequestOperationWithRequest:requestImageURL imageProcessingBlock:^UIImage *(UIImage *image) { return image; } success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { if( message.image ) { NSLog( @"updating imageInfo for message" ); [Utilities updateImageInfo:message.image withImage:image asPreview:YES asThumbnail:YES preserveSize:YES]; } else { NSLog( @"creating a new imageInfo for message" ); ImageInfo * imageInfo = [Utilities createImageInfoFromImage:image asPreview:NO asThumbnail:NO preserveSize:YES]; if( imageInfo.managedObjectContext == nil ) NSLog( @"imageInfo MOC is NIL" ); else if( message.managedObjectContext == nil ) { NSLog( @"message MOC is NIL" ); message = [[DataController sharedController] convoMessageForKey:message.key]; if( !message ) NSLog( @"message is NIL, meaning it wasn't found in the MOC" ); else if( !message.managedObjectContext ) NSLog( @"message MOC was STILL NIL" ); else NSLog( @"problem solved..." ); } if( imageInfo ) [[DataController sharedController] associateImageInfo:imageInfo withMessage:message]; } [[DataController sharedController] saveContext]; } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog( @"Image DOWNLOAD error... \n%@" , [NSString stringWithFormat:@"%@" , error] ); }]; [imageOperation start]; } for( id<CommsObserver> observer in _observers ) [observer newConvoMessages:@[message.key]]; } } // End for loop of messageList } // End if JSONresponse } // End outer if ERROR statement } // End Success failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { NSLog( @"Error: \n%@" , [NSString stringWithFormat:@"%@" , error] ); } ]; [operation start];
您需要确保调用与托pipe对象上下文关联的方法的执行上下文与托pipe对象上下文相适合(即相同 )。
也就是说,当你调用
[[DataController sharedController] saveContext];
执行(最终)方法save:
的线程(或分派队列)必须与被pipe对象上下文关联的地方相同。
在这种情况下,我们可以立即得出结论:这只能工作在IFF a)AFN的完成处理程序将在主线程上执行AND b)pipe理对象上下文也与主线程相关联,或者你负责这在执行saveContext
和使用performBlock:
或performBlockAndWait:
saveContext
否则,由于托pipe对象上下文的执行上下文是私有的 ,所以任何完成处理器的执行上下文都不会匹配这个上下文。 因此,您违反了Core Data的并发规则。
无论何时向托pipe对象或托pipe对象上下文发送消息,都需要确保当前的执行上下文将是正确的 。 也就是说,您需要使用performBlock:
或者performBlockAndWait:
并将访问包装到块中:
[[DataController sharedController].managedObjectContext performBlock:^{ assert(message.managedObjectContext == [DataController sharedController].managedObjectContext); message.unread = [NSNumber numberWithBool:YES]; [[DataController sharedController] saveContext]; ... }];
注意:除了托pipe对象的属性objectID
之外,您必须将所有这些访问包装到performBlock:
或performBlockAndWait:
。
objectID
可以从任何线程获得。 因此,只要您有objectID
就可以使用它将任何托pipe对象提取到任何上下文中。
其他一些提示:
使用托pipe对象
您需要确保在使用托pipe对象(即向其发送消息)时,将在与托pipe对象的托pipe对象上下文关联的相同执行上下文中执行此操作。
也就是说,为了确保你使用performBlock:
或者performBlockAndWait:
如下:
NSManagedObjectContext* context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
注意: context
使用私有队列。
__block NSManagedObject* obj; [context performBlockAndWait:^{ obj = [context objectRegisteredForID:theObjectID]; }];
假设,以下语句将在任意线程上执行,这是不安全的:
NSString* name = obj.name;
“不安全”,除非你知道obj的托pipe对象上下文已经被关联到主线程上面的语句也会在主线程上执行。 如果上下文使用私有队列,那么除非使用performBlock:
或performBlockAndWait:
::
安全:
__block NSString* name; [obj.managedObjectContext performBlockAndWait:^{ name = obj.name; }];
从任何线程获取objectID始终是安全的:
NSManagedObjectID* moid = obj.objectID; // safe from any thread
将pipe理对象从一个上下文移动到另一个上下文:
您不能在上下文B中使用与上下文A关联的托pipe对象。为了将该对象“移动”到上下文B中,首先需要objectID
,然后在上下文B中“获取”此对象:
NSManagedObjectID* moid = obj.objectID NSManagedObjectContext* otherContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [otherContext performBlock:^{ NSManagedObject* obj = [otherContext objectWithID: moid]; ... }];
错误参数
慎重处理错误参数。
错误参数始终是自动发布的。 performBlockAndWait:
内部不使用自动释放池。 所以,你可以在块外面有一个__blockvariables错误 :
__block NSManagedObject* obj; __block NSError* error; [context performBlockAndWait:^{ obj = [context existingObjectWithID:theObjectID error:&error]; }]; if (obj==nil) { NSLog(@"Error:%@", error); }
然而performBlock:
将在内部使用一个autorelease池! 这有后果:
如果使用asynchronous版本performBlock:
,则需要处理该块内的错误:
__block NSManagedObject* obj; [context performBlock:^{ NSError* error; obj = [context existingObjectWithID:theObjectID error:&error]; if (obj==nil) { NSLog(@"Error:%@", error); } }];
- 如何在iOS中asynchronous下载图像?
- 与NSURLSessionasynchronous上传将无法正常工作,但同步NSURLConnection不会
- 由于等待,dispatch_semaphore_signal从不被执行
- asynchronous获取Swift Xcode
- 核心数据加载Swift后asynchronous刷新UITableView
- 如何在返回variables之前等待firebase将数据加载到variables中然后调用函数
- 如何在下载前检查是否有新版本的图像?
- asynchronousswift 3
- 点击或滚动后,asynchronous下载的图像只出现在UITableView中