gpt4 book ai didi

ios - iOS 中的 NSOperation - 如何处理循环和嵌套的 NSOperation 调用以获取图像

转载 作者:塔克拉玛干 更新时间:2023-11-02 20:09:21 28 4
gpt4 key购买 nike

我需要帮助了解如何适当处理以下用例:

假设我正在编写一个聊天应用:

  1. 启动应用
  2. 请求服务器 (AFHTTPRequestOperation) 给我一个所有新消息的列表
  3. 遍历这些消息以查看我是否需要下载任何图像
  4. 如果是,则再次调用服务器(AFImageRequestOperation)获取图像

当我的托管对象“消息”不再处于同一上下文中时,我不断遇到崩溃,但我只使用了一个 managedObjectContext

是不是因为我嵌套调用的方式,因为它们是异步的?我几乎可以肯定消息不会在其他地方被删除,因为我看到了。

需要注意的一件事是,当我更改 View Controller 时,这似乎会发生。我启动该应用程序,并在 Root View Controller (RVC) 中执行上面的步骤 #2。如果我在所有图像下载完成之前触摸“MessageListViewController”(MLVC),那些未完成下载的图像的关联消息突然有一个 nil 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];

最佳答案

您需要确保调用与托管对象上下文关联的方法的执行上下文与托管对象上下文是适当的(即相同)。

也就是说,当你调用

 [[DataController sharedController] saveContext];

将执行方法save: 的线程(或调度队列)(最终)必须与托管对象上下文相关联的线程(或调度队列)相同。

在这种情况下,我们可以立即得出结论,这仅适用于 IFF a) AFN 的完成处理程序将在主线程上执行,并且 b) 托管对象上下文也与主线程相关联,或者你在 saveContext 的实现中处理这个问题并使用 performBlock:performBlockAndWait:

否则,由于托管对象上下文的执行上下文是私有(private)任何 完成处理程序的执行上下文将永远不会匹配这个。因此,您违反了 Core Data 的并发规则。

无论何时向托管对象或托管对象上下文发送消息,您都需要确保当前执行上下文正确的。也就是说,您需要使用 performBlock:performBlockAndWait: 并将访问包装到 block 中:

[[DataController sharedController].managedObjectContext performBlock:^{
assert(message.managedObjectContext == [DataController sharedController].managedObjectContext);
message.unread = [NSNumber numberWithBool:YES];
[[DataController sharedController] saveContext];
...

}];

注意:您必须将所有这些访问包装到performBlock:performBlockAndWait: 中,属性objectID 除外 一个托管对象。

objectID 可以从任何线程获得。因此,只要您拥有 objectID,就可以使用它来将任何托管对象提取到任何上下文中。


其他一些提示:

使用托管对象

您需要确保当您使用一个托管对象(即向它发送消息)时,这将在与托管对象的托管对象相关联的相同执行上下文中执行上下文。

也就是说,为了确保,您使用performBlock:performBlockAndWait: 如下:

NSManagedObjectContext* context =  [[NSManagedObjectContext alloc] 
initWithConcurrencyType:NSPrivateQueueConcurrencyType];

注意:context 使用私有(private)队列。

__block NSManagedObject* obj;
[context performBlockAndWait:^{
obj = [context objectRegisteredForID:theObjectID];
}];

假设,以下语句将在任意线程上执行,这是不安全的:

NSString* name = obj.name;

“不安全”,除非你知道 obj 的托管对象上下文已经关联到主线程并且上面的语句也会在主线程上执行。如果上下文使用私有(private)队列,这将永远为真,除非您使用performBlock:performBlockAndWait::

安全:

__block NSString* name;
[obj.managedObjectContext performBlockAndWait:^{
name = obj.name;
}];

从任何线程获取 objectID 总是安全的:

NSManagedObjectID* moid = obj.objectID;  // safe from any thread

将托管对象从一个上下文“移动”到另一个上下文:

您不能在上下文 B 中使用关联到上下文 A 的托管对象。为了将该对象“移动”到上下文 B 中,您首先需要 objectID,然后在上下文中“获取”该对象乙:

NSManagedObjectID* moid = obj.objectID

NSManagedObjectContext* otherContext = [[NSManagedObjectContext alloc]
initWithConcurrencyType:NSPrivateQueueConcurrencyType];

[otherContext performBlock:^{
NSManagedObject* obj = [otherContext objectWithID: moid];
...
}];

错误参数

小心错误参数。

错误参数总是自动释放。 performBlockAndWait: 内部不使用自动释放池。所以,你可以在 block 外有一个 __block 变量error:

__block NSManagedObject* obj;
__block NSError* error;
[context performBlockAndWait:^{
obj = [context existingObjectWithID:theObjectID error:&error];
}];
if (obj==nil) {
NSLog(@"Error:%@", error);
}

但是 performBlock: 将在内部使用自动释放池!这会产生以下后果:

如果您使用异步版本performBlock:,您需要在 block 内处理错误:

__block NSManagedObject* obj;
[context performBlock:^{
NSError* error;
obj = [context existingObjectWithID:theObjectID error:&error];
if (obj==nil) {
NSLog(@"Error:%@", error);
}
}];

关于ios - iOS 中的 NSOperation - 如何处理循环和嵌套的 NSOperation 调用以获取图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20171875/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com