- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我需要帮助了解如何适当处理以下用例:
假设我正在编写一个聊天应用:
AFHTTPRequestOperation
) 给我一个所有新消息的列表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/
是否可以将数据从 NSOperation 传递到下一个 NSOperation 使用的依赖链? 谢谢 克里斯 最佳答案 是的。当前NSOperation可以通过 dependencies 访问它的依赖
我有两个 NSOperation 负责下载和解析。下载操作成功后,我收到一些 NSData 我想将该数据设置为解析操作要使用的数据: init(context: NSManagedObjectCont
我的应用程序获取当前设备位置,将其发布到我的服务器,并返回要在 TableView 中显示的字典。目前我正在使用 CLLocationManager 委托(delegate)方法和 AFJSONReq
我正在编写一个网络连接的应用程序,它需要执行多个异步请求来加载依赖树中较低层所需的数据。 图 1. 出于可视化目的,考虑一个 ASIHTTPRequests A、B、C、D、E 和 F 的示例: A的
我有一个相当简单但昂贵的任务需要在后台运行,这是标准的 NSOperation 情况。我还需要确保该操作支持取消并适当停止。鉴于该要求,哪种方法更好:只是将昂贵的方法调用包装在 NSInvocatio
今天,我在尝试“概括”我的“CoreData 导入操作”时遇到了一个奇怪的问题。看来如果我创建 NSOperation 的通用子类 main() func 不会被调用。 简单的例子: class My
我需要帮助了解如何适当处理以下用例: 假设我正在编写一个聊天应用: 启动应用 请求服务器 (AFHTTPRequestOperation) 给我一个所有新消息的列表 遍历这些消息以查看我是否需要下载任
我想在当前执行的线程上同步执行一个 NSOperation。我可以只调用 [NSOperation start] 吗?这是否总是在当前正在执行的线程中运行? 另一种方法是创建一个 NSOperatio
我有一个应用程序,其中一个长时间运行的进程(> 1 分钟)被放置在 NSOperationQueue(队列 A)上。当队列 A 操作运行时,UI 完全响应,完全符合预期。 但是,我有一种用户可以执行的
我正在使用UIActivityItemProvider子类提供自定义数据。但是有时获取数据失败,并且我不想展示事件(例如消息编写器)。在[self cancel]方法中尝试了return nil;和i
根据关于 NSOperation 的 Apple 文档,我们必须覆盖 main非并发操作的方法和start并发操作的方法。但为什么? 最佳答案 首先,请记住,“并发”和“非并发”在 NSOperati
我正在开发一个 iPad 应用程序。它使用 NSOperation 在后台下载某些内容,并由 NSOperationQueue 处理。我发现,除非我向 NSOperation 添加保留,否则在执行操作
我将创建一系列 NSOperation 并在队列中运行它们。 它们都是按顺序运行的,一次运行一个。 这些操作将从网络获取数据并创建和保存核心数据管理对象。 如何处理应用程序退出的情况?由于操作在分离线
我在 NSOperation 和观察者方面遇到问题。 我有一个 tabbarcontroller 和一个 splashController。我希望启动画面加载并下载文件,并且在下载文件时使 tabba
我正在从 Facebook Connect 获取一些数据(使用 FBConnect Objective-C 2.0 框架),并且我在 NSOperation 中完成所有这些操作。它位于 NSOpera
我正在从NavigationController的 subview 中调用NSOperation。 MyOperation *op = [[MyOperation alloc] target:self
我是iPhone新手。在哪里可以找到NSOperationQueue和NSOperation的示例?NSOperationQueue和NSOperation与线程相比有什么优势? 谢谢 最佳答案 阅读
我对 NSOperations 有疑问。一切正常,但有时(我不知道为什么)操作 block 被简单地跳过。我错过了什么吗?操作怎么可能不是 NSLogging “输入操作”?以下是 viewDidLo
我一直在搜索,但只能找到委托(delegate)模式的想法来从 NSOperation 传回数据。我有一个 NSOperation 在完成该 NSOperation 后下载数据我希望它传递回将其下载的
我正在使用后台操作来发出 URL 请求。如果操作返回错误,如何通知我,然后将相同的操作类型重新添加到队列中以重试? 最佳答案 您可以使用 NSNotification 来实现这一点。创建通知并在请求失
我是一名优秀的程序员,十分优秀!