- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我希望能够序列化“真正的”异步方法,例如:
这通常是一项棘手的工作,大多数串行队列示例在 NSBlockOperation 的 block 中显示“ sleep ”。这是行不通的,因为操作只有在回调发生时才完成。
我已经尝试通过子类化 NSOperation 来实现它,这里是实现中最有趣的部分:
+ (MYOperation *)operationWithBlock:(CompleteBlock)block
{
MYOperation *operation = [[MYOperation alloc] init];
operation.block = block;
return operation;
}
- (void)start
{
[self willChangeValueForKey:@"isExecuting"];
self.executing = YES;
[self didChangeValueForKey:@"isExecuting"];
if (self.block) {
self.block(self);
}
}
- (void)finish
{
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
self.executing = NO;
self.finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
- (BOOL)isFinished
{
return self.finished;
}
- (BOOL) isExecuting
{
return self.executing;
}
效果很好,这是一个演示...
NSOperationQueue *q = [[NSOperationQueue alloc] init];
q.maxConcurrentOperationCount = 1;
dispatch_queue_t queue = dispatch_queue_create("1", NULL);
dispatch_queue_t queue2 = dispatch_queue_create("2", NULL);
MYOperation *op = [MYOperation operationWithBlock:^(MYOperation *o) {
NSLog(@"1...");
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1");
[o finish]; // this signals we're done
});
}];
MYOperation *op2 = [MYOperation operationWithBlock:^(MYOperation *o) {
NSLog(@"2...");
dispatch_async(queue2, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2");
[o finish]; // this signals we're done
});
}];
[q addOperations:@[op, op2] waitUntilFinished:YES];
[NSThread sleepForTimeInterval:5];
请注意,我还使用了 sleep ,但确保它们在后台线程中执行以模拟网络调用。日志内容如下
1...
1
2...
2
如愿以偿。这种方法有什么问题?有什么我应该注意的注意事项吗?
最佳答案
“序列化”异步任务实际上将被命名为“延续”(另请参阅此 wiki 文章 Continuation。
假设,您的任务可以定义为带有完成处理程序的异步函数/方法,其参数是异步任务的最终结果,例如:
typedef void(^completion_handler_t)(id result);
-(void) webRequestWithCompletion:(completion_handler_t)completionHandler;
-(void) showAlertViewWithResult:(id)result completion:(completion_handler_t)completionHandler;
拥有blocks可用,通过从上一个任务的完成 block 中调用下一个异步任务可以轻松完成“继续”:
- (void) foo
{
[self webRequestWithCompletion:^(id result) {
[self showAlertViewWithResult:result completion:^(id userAnswer) {
NSLog(@"User answered with: %@", userAnswer);
}
}
}
注意方法 foo
被“异步”感染;)
也就是说,这里是方法的最终效果foo
,即将用户的答案打印到控制台,实际上也是异步的。
但是,“链接”多个异步任务,即“继续”多个异步任务,可能会很快变得笨拙:
使用完成 block 实现“延续”将增加每个任务的完成处理程序的缩进。此外,实现一种让用户在任何状态下取消任务的方法,并实现处理错误情况的代码,代码很快就会变得困惑,难以编写和理解。
实现“继续”以及取消和错误处理的更好方法是使用 Futures or Promises 的概念。 . Future 或 Promise 表示异步任务的最终结果。基本上,这只是一种向调用站点“发出最终结果信号”的不同方法。
在 Objective-C 中,“Promise”可以作为普通类来实现。有第三方图书馆实现“ promise ”。以下代码使用了特定的实现,RXPromise。
在使用这样的Promise 时,您可以按如下方式定义您的任务:
-(Promise*) webRequestWithCompletion;
-(Promise*) showAlertViewWithResult:(id)result;
注意:没有完成处理程序。
使用Promise,异步任务的“结果”将通过“成功”或“错误”处理程序获得,该处理程序将“注册”到then
。 promise 的属性(property)。成功或错误处理程序在任务完成时被调用:当它成功完成时,成功处理程序将被调用,将其结果传递给成功处理程序的参数 result。否则,当任务失败时,它会将原因传递给错误处理程序——通常是 NSError
。对象。
Promise 的基本用法如下:
Promise* promise = [self asyncTasks];
// register handler blocks with "then":
Promise* handlerPromise = promise.then( <success handler block>, <error handler block> );
成功处理程序 block 有一个类型为 id
的参数 result .错误处理程序 block 有一个类型为 NSError
的参数.
注意语句promise.then(...)
返回一个代表任一处理程序结果的 promise ,当“父” promise 已成功或错误地解决时调用它。处理程序的返回值可以是“立即结果”(某个对象)或“最终结果”——表示为 Promise 对象。
以下代码片段(包括复杂的错误处理)显示了 OP 问题的注释示例:
- (void) foo
{
[self webRequestWithCompletion] // returns a "Promise" object which has a property "then"
// when the task finished, then:
.then(^id(id result) {
// on succeess:
// param "result" is the result of method "webRequestWithCompletion"
return [self showAlertViewWithResult:result]; // note: returns a promise
}, nil /*error handler not defined, fall through to the next defined error handler */ )
// when either of the previous handler finished, then:
.then(^id(id userAnswer) {
NSLog(@"User answered with: %@", userAnswer);
return nil; // handler's result not used, thus nil.
}, nil)
// when either of the previous handler finished, then:
.then(nil /*success handler not defined*/,
^id(NEError* error) {
// on error
// Error handler. Last error handler catches all errors.
// That is, either a web request error or perhaps the user cancelled (which results in rejecting the promise with a "User Cancelled" error)
return nil; // result of this error handler not used anywhere.
});
}
代码当然需要更多的解释。有关详细和更全面的描述,以及如何在任何时间点完成取消,您可以查看 RXPromise library - 实现“Promise”的 Objective-C 类。披露:我是 RXPromise 库的作者。
关于ios - 在 Objective-C 中序列化异步任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18202952/
IO 设备如何知道属于它的内存中的值在memory mapped IO 中发生了变化? ? 例如,假设内存地址 0 专用于保存 VGA 设备的背景颜色。当我们更改 memory[0] 中的值时,VGA
我目前正在开发一个使用Facebook sdk登录(通过FBLoginView)的iOS应用。 一切正常,除了那些拥有较旧版本的facebook的人。 当他们按下“使用Facebook登录”按钮时,他
假设我有: this - is an - example - with some - dashesNSRange将使用`rangeOfString:@“-”拾取“-”的第一个实例,但是如果我只想要最后
Card.io SDK提供以下详细信息: 卡号,有效期,月份,年份,CVV和邮政编码。 如何从此SDK获取国家名称。 - (void)userDidProvideCreditCardInfo:(Car
iOS 应用程序如何从网络服务下载图片并在安装过程中将它们安装到用户的 iOS 设备上?可能吗? 最佳答案 您无法控制应用在用户设备上的安装,因此无法在安装过程中下载其他数据。 只需在安装后首次启动应
我曾经开发过一款企业版 iOS 产品,我们公司曾将其出售给大型企业,供他们的员工使用。 该应用程序通过 AppStore 提供,企业用户获得了公司特定的配置文件(包含应用程序配置文件)以启用他们有权使
我正在尝试将 Card.io SDK 集成到我的 iOS 应用程序中。我想为 CardIO ui 做一个简单的本地化,如更改取消按钮标题或“在此保留信用卡”提示文本。 我在 github 上找到了这个
我正在使用 CardIOView 和 CardIOViewDelegate 类,没有可以设置为 YES 的 BOOL 来扫描 collectCardholderName。我可以看到它在 CardIOP
我有一个集成了通话工具包的 voip 应用程序。每次我从我的 voip 应用程序调用时,都会在 native 电话应用程序中创建一个新的最近通话记录。我在 voip 应用程序中也有自定义联系人(电话应
iOS 应用程序如何知道应用程序打开时屏幕上是否已经有键盘?应用程序运行后,它可以接收键盘显示/隐藏通知。但是,如果应用程序在分屏模式下作为辅助应用程序打开,而主应用程序已经显示键盘,则辅助应用程序不
我在模拟器中收到以下错误: ImageIO: CGImageReadSessionGetCachedImageBlockData *** CGImageReadSessionGetCachedIm
如 Apple 文档所示,可以通过 EAAccessory Framework 与经过认证的配件(由 Apple 认证)进行通信。但是我有点困惑,因为一些帖子告诉我它也可以通过 CoreBluetoo
尽管现在的调试器已经很不错了,但有时找出应用程序中正在发生的事情的最好方法仍然是古老的 NSLog。当您连接到计算机时,这样做很容易; Xcode 会帮助弹出日志查看器面板,然后就可以了。当您不在办公
在我的 iOS 应用程序中,我定义了一些兴趣点。其中一些有一个 Kontakt.io 信标的名称,它绑定(bind)到一个特定的 PoI(我的意思是通常贴在信标标签上的名称)。现在我想在附近发现信标,
我正在为警报提示创建一个 trigger.io 插件。尝试从警报提示返回数据。这是我的代码: // Prompt + (void)show_prompt:(ForgeTask*)task{
您好,我是 Apple iOS 的新手。我阅读并搜索了很多关于推送通知的文章,但我没有发现任何关于 APNS 从 io4 到 ios 6 的新更新的信息。任何人都可以向我提供 APNS 如何在 ios
UITabBar 的高度似乎在 iOS 7 和 8/9/10/11 之间发生了变化。我发布这个问题是为了让其他人轻松找到答案。 那么:在 iPhone 和 iPad 上的 iOS 8/9/10/11
我想我可以针对不同的 iOS 版本使用不同的 Storyboard。 由于 UI 的差异,我将创建下一个 Storyboard: Main_iPhone.storyboard Main_iPad.st
我正在写一些东西,我将使用设备的 iTunes 库中的一部分音轨来覆盖 2 个视频的组合,例如: AVMutableComposition* mixComposition = [[AVMutableC
我创建了一个简单的 iOS 程序,可以顺利编译并在 iPad 模拟器上运行良好。当我告诉 XCode 4 使用我连接的 iPad 设备时,无法编译相同的程序。问题似乎是当我尝试使用附加的 iPad 时
我是一名优秀的程序员,十分优秀!