ReactiveCocoa要点:理解和使用RACCommand

这篇文章附带的源代码在github: https://github.com/olegam/RACCommandExample

是RACCommand新的最好的朋友?

RACCommand是最重要的部分之一ReactiveCocoa最终可以节省你大量的时间和帮助使你的iOS或OS X应用程序更健壮。

我见过几个人新ReactiveCocoa(以下略RAC)不完全了解RACCommand工作时,它应该被使用。 所以我认为这将是有用的写一个小介绍了光。 官方文档不给很多如何使用RACCommand的例子,但是评论在头文件是伟大的。 然而,他们可能很难理解如果你新RAC。

RACCommand类是用来表示一些行动的执行。 经常引发的执行一些动作在UI中。 如当用户水龙头一个按钮。RACCommand可以配置为处理实例推理何时可以执行。 这可以很容易地绑定到UI和命令还将确保它不会开始执行时不启用。 常用的策略命令可以执行时离开allowsConcurrentExecution在它的默认值没有。 这将确保该命令不重新开始执行是否已经执行。 命令执行的结果是作为一个表示RACSignal并因此产生的结果下一个:(代表新值或结果),完成错误:。 下面,我将展示如何使用这个。

示例应用程序

让我们假设我们是在做一项简单的iOS应用程序可以让用户订阅的电子邮件列表。 在最简单的形式,我们将有一个文本字段和一个按钮。 当用户进入了他的电子邮件和水龙头按钮应该张贴一些webservice电子邮件地址。 很容易。 但是有一些优势情况下,我们应该确保处理提供最好的体验。 如果用户水龙头按钮两次会发生什么? 错误是怎么处理的? 如果电子邮件是无效的?RACCommand可以帮助我们处理这些案件。 我实现了一个小型应用程序来演示在这篇文章中讨论的概念。

这里的示例应用程序的源代码:https://github.com/olegam/RACCommandExample

用一个非常简单的视图控制器应用还演示了一种实践MVVM模式iOS应用程序。 基本视图控制器设置视图层次和实例化视图模型的一个实例。

- (void)bindWithViewModel {  RAC(self.viewModel, email) = emailTextField.rac_textSignal;  subscribeButton.rac_command = viewModel.subscribeCommand;  statusLabel,210)!important">text) = RACObserve(statusMessage); } 

上面的方法(称为viewDidLoad)创建视图和视图模型之间的绑定。 所有有趣的东西在视图模型。 它具有以下界面:

@interface SubscribeViewModel : NSObject  @property(nonatomic,210)!important">strong) RACCommand *subscribeCommand;  // write to this property NSString *email;  // read from this property statusMessage;  @end 

RACCommand属性暴露这是这篇文章的其余部分将是什么。 两个其他的字符串属性绑定到如上所示的属性视图。 全面实施视图模型如下所示:

#import "SubscribeViewModel.h" #import "AFHTTPRequestOperationManager+RACSupport.h" #import "NSString+EmailAdditions.h"  static NSString *const kSubscribeURL = @"http://reactivetest.apiary.io/subscribers";  SubscribeViewModel () RACSignal *emailValidSignal; @end  @implementation SubscribeViewModel  id)init {  self = [super init];  if (self) {  [self mapSubscribeCommandStateToStatusMessage];  }  return self; }  mapSubscribeCommandStateToStatusMessage {  startedMessageSource = [subscribeCommand.executionSignals map:^id(subscribeSignal) {  NSLocalizedString(@"Sending request...", nil);  }];   completedMessageSource = [flattenMap:^RACStream *(return [[[subscribeSignal materialize] filter:^BOOL(RACEvent *event) {  event.eventType == RACEventTypeCompleted;  }] id(id value) {  @"Thanks",0)!important">nil);  }];  }];   failedMessageSource = [[errors subscribeOn:[RACScheduler mainThreadScheduler]] NSError *error) {  @"Error :(",210)!important">self,210)!important">statusMessage) = [RACSignal merge:@[startedMessageSource,210)!important">completedMessageSource,210)!important">failedMessageSource]]; }  - (RACCommand *)subscribeCommand {  if (!_subscribeCommand) {  email = email;  _subscribeCommand = [[RACCommand alloc] initWithEnabled:emailValidSignal signalBlock:^RACSignal *(input) {  return [SubscribeViewModel postEmail:email];  }];  }  _subscribeCommand; }  + (RACSignal *)postEmail:(NSString *)email {  AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];  manager.requestSerializer = [AFJSONRequestSerializer new];  NSDictionary *body = @{@"email": email ?: @""};  manager rac_POST:kSubscribeURL parameters:body] logError] replayLazily]; }  emailValidSignal {  _emailValidSignal) {  _emailValidSignal = [email) email) {  return @([email isValidEmail]);  }];  }  _emailValidSignal; }  @end 

这可能看起来像一大块让我们通过更小的部分。 的RACCommand我们最感兴趣的是这样的:

_subscribeCommand; } 

命令初始化的enabledSignal参数。 这是一个信号指示时,可以执行的命令。 在我们的例子中它应该被允许执行时,用户输入的电子邮件地址是有效的。 的self.emailValidSignal是一个信号,发送一个没有或者一个是的每次邮件更改。

signalBlock每次调用参数时需要执行的命令。 块应该返回一个信号代表执行。 自从我们离开的默认属性allowsConcurrentExecution没有命令将看这个信号,不允许任何新的死刑执行之前完成进度。

因为命令按钮的设置rac_command属性中定义的UIButtton + RACCommandSupport类别按钮将自动启用和禁用状态之间变化时基于命令可以执行。

还命令按钮时将自动执行用户了。 我们免费得到这一切RACCommand。 如果你需要手动执行命令你可以通过消息传递——[RACCommand执行:]。 参数是一个可选的输入。 我们这里不使用它,但它通常是非常有用的(按钮发送本身作为输入时调用-execute:)。 的-execute:的方法也是一个地方你可以把看执行的状态。 你可能是这样的:

[[subscribeCommand execute:nil] subscribeCompleted:^{  NSLog(@"The command executed"); }]; 

在我们的示例中按钮执行命令(所以我们别叫-execute:),因此我们必须倾听另一个属性的命令来更新UI时执行的命令。 有几个机会之间的选择。 这也许会让人有些迷惑。 的executionSignals的属性RACCommand是一个信号,85)">下一个:每一次的命令开始执行。 参数是信号产生的命令。 所以这是一个信号的信号。 我们使用的mapSubscribeCommandStateToStatusMessage视图模型的方法得到一个信号与一个字符串值每次启动命令:

nil); }]; 

得到类似的信号与一个字符串命令完成每次执行我们必须做更多的工作,如果我们想要纯粹的功能:

nil);  }]; }]; 

flattenMap:方法调用的块subscribeSignal当命令执行。 这个块返回一个新的信号和它的值传递到由此产生的信号。 的实现操作符让我们得到的信号RACEvent(即。 的下一个:完整的错误:消息传递RACEvent作为实例下一个:值产生的信号)。 我们可以过滤这些事件只会从当信号完成的,在这种情况下将其映射到一个字符串值。 我松了你吗? 我希望不是这样,但您可能需要查找的文档flattenMap:实现为了更好地理解他们所做的事情。

我们可以用不同的方式来实现这一行为,更少的功能,但可能更容易理解:

@weakify(self); [subscribeNext:^(subscribeSignal) {  [subscribeSignal subscribeCompleted:^{  strongify(self);  statusMessage = @"Thanks";  }]; }]; 

然而,我不喜欢上面的实现,因为它涉及到副作用的街区。 这也有提到的缺点和保留自我块。 因此我必须使用@weakify@strongify宏(中定义libextobjcpod)以避免保留周期。 所以更好的只是尽可能完全避免副作用与原始实现像我一样。

有一个重要的细节需要注意的executionSignals财产。 这里的信号不包括错误事件。 对于那些有一个特别的错误财产。 一个信号,发送在执行命令的任何错误下一个:。 不像定期发送的错误错误:事件,终止信号。 我们可以很容易的错误映射到字符串消息:

nil); }]; 

现在,当我们有3个信号状态信息我们想展示给用户可以将它们合并成一个信号和绑定statusMessage(绑定到属性的视图模型statusLabel.text属性的视图控制器)。

failedMessageSource]]; 

这是如何的一个例子RACCommand可用于实践的iOS应用程序。我认为这种方式实现逻辑有很多优势在许多人会实现它的路吗UITextFieldDelegate在视图控制器和大量的状态存储在实例变量或属性。

感兴趣的其他RACCommand细节

RACCommand有一个执行属性,实际上是一个信号发送是的执行:调用和没有当它终止。 在订阅的信号将发送它的当前值。 如果你只需要得到的当前值和不需要一个信号,你可以立即:

BOOL commandIsExecuting = [[command.executing first] boolValue]; 

启用房地产也是一个信号发送是的没有。 它将发送没有当命令创建了一个enabledSignal,发送一个信号没有或者是执行和信号allowsConcurrentExecutions被设置为没有

如果你想消息-execute:在不启用一个信号,表明它将立即发送一个错误,但错误不会被发送到错误信号。

-execute:方法将自动订阅原始信号和多播。 这基本上意味着你没有订阅返回的信号,但是如果你这样做你不应该害怕副作用发生两次。

12月5日th,2013年ReactiveCocoa

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom