- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
通常,在 GUI 中,我遇到的程序流程本质上是顺序的,但涉及异步操作(例如 NSURLConnection
下载东西)和 UI 操作(等待用户选择一个UIActionSheet
中的选项)。
举例说明,
UIActionSheet
,提示用户选择颜色。UIAlertView
,等待点击OK)UIAlertView
中选择 YES,请重试下载。流程是顺序的 - 我们不会在 1 之前执行 2。但是由于异步操作和 UI 元素,如果我们使用委托(delegate)(或 block ),这很快就会变成意大利面条代码。
是否有编写此类代码的通用方法?
最佳答案
有一个图书馆叫Reactive Cocoa这太棒了,但很难习惯。
实现目标的一种更简单但不是那么棒的方法是使用 blocks wrapper围绕 UIAlertView 和 UIActionSheet。此外,这还假设您的网络代码中有回调 block 。
例子:
- (void)showActionSheet
{
BlockActionSheet *sheet = [BlockActionSheet sheetWithTitle:@"Choose one"];
__weak BlockActionSheet *weakSheet = sheet;
[sheet addButtonWithTitle:@"Blue" atIndex:0 block:^{
[self downloadFileFromServerSuccessBlock:^{
//YAY
} failureBlock:^{
BlockAlertView *alert = [BlockAlertView alertWithTitle:@"Failure" message:@"Something Went Wrong"];
[alert addButtonWithTitle:@"Try Again" block:^{
[weakSheet showInView:self.view];
}];
[alert setCancelButtonWithTitle:@"Cancel" block:nil];
}];
}];
[sheet addButtonWithTitle:@"Black" atIndex:1 block:^{
//something else
}];
[sheet setCancelButtonWithTitle:@"Cancel" block:nil];
[sheet showInView:self.view];
}
所以最后一行“[sheet showInView:self.view]”开始了整个过程。如果他们选择蓝色,那么该 block 将被调用。您的网络代码还支持 block ,因此您可以从那里获得成功和失败的 block 回调。如果失败,那么您会弹出一个基于 block 的警报 View ,使 ActionSheet 再次显示自己。
我希望这至少对您有所帮助。此外,“self.view”调用可能会发生一些强引用,因此我也会对 View 进行弱引用。
这是 ReactiveCocoa 的例子。我对这个框架很陌生,所以我希望我能正确使用它。
/*
* This function is getting called from a login view controller.
*/
- (void)sendAuthentication
{
/*
* A RACSubscribable will invoke the where^ block when it calls [sendNext:];
* That call is coming from the ThaweQBRequestController.
* If the where^ block return YES then the subscribeNext^ block will get called.
* asMaybes wraps the object I send back in a RACMaybe object which has a convenience method [hasObject];
* The subscribeNext^ block has an object coming into it, that object is an array in this case that I sent
* from the ThaweQBRequestController.
*/
RACSubscribable *sub = [[ThaweQBRequestController logInWithUsername:self.thaweusername password:self.thawepassword] asMaybes];
[[sub where:^(id x) {
return [x hasObject];
}]
subscribeNext:^(id _) {
NSArray *array = [_ object];
NSString *errcode = [array objectAtIndex:0];
if (errcode.boolValue == YES)
//YAY SUCCESS!!!
} else {
//UH OH
}
// Update UI, the array has the username and password in it if I want to display or whatever is clever.
}];
}
然后我们进入我的网络请求 Controller 。
+ (RACAsyncSubject *)logInWithUsername:(NSString *)username password:(NSString *)password
{
/*
* First we have our loginCommand. It will invoke the block we give it on a background thread.
* This block returns a Subscribable. We can subscribe other blocks to be invoked when the aync call is done.
* In this case we are creating loginResult to retrieve the async call.
*/
RACAsyncCommand *loginCommand = [RACAsyncCommand command];
/*
* Now we have our AsyncSubject. We are instantiated it here and are going to pass it back to the login view controller;
* When our async call finishes and we filter through the results we will call [subject sendNext:] to invoke blocks we created in
* the login view controller;
* We will filter the results uses the loginResult blocks.
*/
RACAsyncSubject *subject = [RACAsyncSubject subject];
/*
* loginResult is a "Subscribable". Every time it gets a [sendNext:] call it runs the blocks assosiated with it.
* These [sendNext:] calls are coming from our network code.
* 'repeat' means that even after the async block is invoked it keeps a reference to it incase we want to use it again.
* 'asMaybes' wraps a RACMaybe object around the object you send to the loginResult blocks. The benefit of a RACMaybe is
* that it has some convienence methods like 'hasError' and 'hasObject'.
*/
RACSubscribable *loginResult = [[[loginCommand
addAsyncBlock:^(id _) {
return [self authenticateUser:username password:password];
}]
repeat]
asMaybes];
/*
* The first block, where^, get called every time loginResult calls [sendNext:].
* If it returns YES then the select^ block gets called. The select^ block invokes the subscribeNext^ block and sends it the
* error.
*/
[[[loginResult
where:^(id x) {
return [x hasError];
}]
select:^(id x) {
return [x error];
}]
subscribeNext:^(id x) {
NSLog(@"network error omg: %@", x);
}];
/*
* Same as above except this time we are looking for instances when we have an object instead of an NSError.
* The object we are getting is being returned from our network code. In this case it is an integer, 0 means we had a successfull login.
* Now we can call [subject sencNext:] to inform our login view controller of the pass or fail of the authentication.
* [sendCompleted] is a cleanup call, allowing ReactiveCocoa to dispose of our blocks and free up memory.
*/
[loginResult
where:^(id x) {
return [x hasObject];
}]
subscribeNext:^(id _) {
NSNumber *number;
NSString *errcode = [_ object];
if (errcode.intValue == 0) number = [NSNumber numberWithBool:YES] ?: [NSNumber numberWithBool:NO];
[subject sendNext:[NSArray arrayWithObjects:number, username, password, nil]];
[subject sendCompleted];
}];
/*
* [execute:] starts the whole thing off. This call invokes the loginCommand block that returns the async call to the loginResult subscribable.
*/
[loginCommand execute:@"This value gets transfered to the addAsyncBlock:^(id _) block above."];
return subject;
}
/*
* This function uses a QuickBase wrapper I made to make server request and whatnot.
* This function is called when the [loginCommand execute:] call in the previous function gets called
* and invokes the loginCommand block which returns the async request.
* That request is what this function is returning.
*/
+ (RACAsyncSubject *)authenticateUser:(NSString *)username password:(NSString *)password
{
QBRequest *request = [[QBRequest alloc] init];
[request setQuickBaseAction:QBActionTypeAuthenticate];
[request setURLString:URLstring withDatabaseID:nil];
[request setApplicationToken:appToken];
return [request sendAndPersist:NO username:username password:password];
}
现在我们在我的实际网络包装器中,它知道请求何时完成或失败或其他什么。
- (RACAsyncSubject *)sendAndPersist:(BOOL)persist username:(NSString *)username password:(NSString *)password
{
self.subject = [RACAsyncSubject subject];
/*
* These next two blocks are called when my network request is done.
* My AsyncSubject is a property so that I can reference it later when I parse
* throught the response and figure out whether I logged in correctly or not.
* In the case that the network call itself fails, then the AysncSubject calls
* [sendError:] which will invoke those NSError capturing blocks in the ThaweQBRequestController.
*/
[anOp onCompletion:^(MKNetworkOperation *completedOperation) {
dispatch_async(background_parseSave_queue(), ^{
[self updateDatabase];
});
} onError:^(NSError *error) {
[subject sendError:error];
}];
[engine enqueueOperation:anOp];
return subject;
}
最后让您了解我何时将主题 [sendNext:] 放入我的解析器中。self.currentParsedCharacterData 是一个具有整数值的 NSString。
else if ([elementName isEqualToString:@"errcode"])
{
if ([self.action isEqualToString:@"API_Authenticate"]) {
[subject sendNext:[self.currentParsedCharacterData copy]];
[subject sendCompleted];
}
}
我知道这很长,但我真的很想给出一些实际的代码示例。
关于objective-c - 具有异步操作和 UI 的顺序执行流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12063636/
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
首先是一些背景;我们正在开发一个数据仓库,并对我们的 ETL 过程使用哪些工具进行一些研究。该团队非常以开发人员为中心,每个人都熟悉 C#。到目前为止,我已经看过 RhinoETL、Pentaho (
我需要具有管理员权限的进程。从this问题和答案来看,似乎没有比启动单独进程更好的方法了。因为我宁愿有一个专用于该过程的过程,而不是仅为此方法在第二个过程中启动我的原始应用程序–我以为我会在VS201
我有这个函数来压平对象 export function flattenObject(object: Object, prefix: string = "") { return Object.key
我正在开发一个基于java的Web应用程序,它要求我使用来自SIP( session 启动协议(protocol))消息的输入生成序列图。我必须表示不同电话和相应服务器之间的调用流程。我可以利用任何工
这是我的代码: Process p=Runtime.getRuntime().exec("something command"); String s; JFrame frame = new JFram
我对 istio 的 mTLS 流程有点困惑。在bookinginfo 示例中,我看到服务通过http 而不是https 进行调用。如果服务之间有 mTLS 那么服务会进行 http 调用吗? 是否可
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
之前做过一个简单的纸牌游戏,对程序的整体流程有自己的想法。我最关心的是卡片触发器。 假设我们有一张名为“Guy”的牌,其效果为“每当你打出另一张牌时,获得 2 点生命”。我将如何将其合并到我的代码中?
我有 4 个 Activity 。 A、B、C 和 D。 用户可以从每个 Activity 开始任何 Activity 。 即 Activity A 有 3 个按钮来启动 B、C 和 D。以同样的方式
我做了一个简单的路由器类,简化后看起来像这样 // @flow import { Container } from 'unstated' type State = { history: Objec
我有两个 Activity ,比如 A1 和 A2。顺序为 A1->A2我从 A1 开始 A2 而没有在 A1 中调用 finish() 。在 A2 中按下后退按钮后,我想在 A1 中触发一个功能。但
我正在考虑在我的下一个项目中使用 BPEL。我试用了 Netbeans BPEL 设计器,我对它很满意。但在我决定使用 BPEL 之前,我想知道它对测试驱动开发的适用程度。不幸的是,我对那个话题知之甚
我需要将两个表格堆叠在一起,前后都有内容。我无法让后面的内容正常流动。堆叠的 table 高度可变。 HTML 结构: ... other content ...
我是 Hibernate 的新手。我无法理解 Hibernate 的流程。请澄清我的疑问。 我有“HibernateUtil.java ”和以下语句 sessionFactory = new Anno
早上好 我开始使用 Ruby,想创建一个小工具来获取我的公共(public) IP 并通过电子邮件发送。我遇到了字符串比较和无法处理的 if/else block 的基本问题。 代码非常简单(见下文)
我目前正尝试在我的团队中建立一个开发流程并阅读有关 GitFlow 的信息。它看起来很有趣,但我可以发现一些问题。 让我们假设以下场景: 我们完成了 F1、F2 和 F3 功能,并将它们 merge
我已经使用 git flow 有一段时间了。我很想了解一个特定的用例。 对于我的一个项目,我有一张新网站功能的门票。此工单取决于许多子任务。我想为主工单创建一个功能分支,然后为每个子任务创建一个脱离父
你怎么知道在一个程序中已经发现并解决了尽可能多的错误? 几年前我读过一篇关于调试的文档(我认为这是某种 HOWTO)。其中,该文档描述了一种技术,其中编程团队故意将错误添加到代码中并将其传递给 QA
我目前正在构建一个微服务架构,并从身份验证服务器和客户端着手。我还想确认使用 token 对用户进行身份验证的最佳流程。 在上图中。第 3 步是我开始感到困惑。我想到了2个解决问题的方法。 每个 ap
我是一名优秀的程序员,十分优秀!