如何建立基于CocoaPods的ReactiveCocoa工程


ReactiveCocoa 和 CocoaPods 一样也是目前很流行的 能方便和提高效率的框架.

其实不光ios 学习任何一门语音最快最直接的方法就是上手. 如果单单是看看文档只能是走马观花得到一个感性认识罢了.前两天之前一个公司的测试人员跟我联系说如何学习JAVA,我问他是怎么学的,他说从网上下了好多视频教程,一直在看视频教程,也买了一些书. 我问他敲了多少代码,他说很少. 其实跟他一样的人不少. 我觉得任何事情都不简单,因为看起来简单的事情自己没有实际做过的话 往往会遇到各种各样的问题. 这也是为什么在工作中制定项目进度计划的时候 我尽量给自己和团队中的人多争取时间的最主要的一个因素.

好了,自己一些啰嗦和感悟. 下面开始正式.

RAV is an Object-C framework for Functional Reavtive Programming; 两个关键点: 1:framework 既然是framework 那就跟你用的其他framework没什么区别 无非就是加到工程中引用罢了.不做多余解释 2:Functional Reavtive Programming 实时响应式编程

虾米引用被阿里收购后现在先上的mac版本的虾米音乐就是用Reactive Cocoa开发的.用阿里人自己的话来说就是

好东西啊,以前我们用 KVO 或 Notification 来自动绑定数据,改用 ReactiveCocoa 写以后,代码结构更佳简单清晰,同时代码行数直接减少 60% 以上

来点直观的对比吧,比如我们想要实现一个需求:当变量中的字符串改变后即时做出相应的反馈 我们用KVO 需要如下这样做

// In your viewDidLoad/awakeFromNib/init
[self addObserver:self 
       forKeyPath:@"someString" 
          options:NSKeyValueObservingOptionNew 
          context:&someStringChangeContext];

// In dealloc
[self removeObserver:self 
          forKeyPath:@"someString" 
             context:&someStringChangeContext];

// Elsewhere in your class
- (void)observeValueForKeyPath:(NSString *)keyPath 
                      ofObject:(id)object 
                        change:(NSDictionary *)change 
                       context:(void *)context
{
    if (context == &someStringChangeContext) {
        if ([keyPath isEqualToString:@"someString"]) {        
            // Do a bunch of stuff here
        }
    }
}

上面一坨代码 在应用Reactive Cocoa后 只有短短一行

[RACObserve(self,someString) distinctUntilChanged] subscribeNext:^(NSString *string) {
    // Do a bunch of things here,just like you would with KVO
}];

因为我之前安装了CocoaPods,所以我这次写的ReactiveCocoaDemo 是基于CocoaPods的.如果没有安装的可以童鞋,可以直接从github上下载ReactiveCocoa

1.打开xcode 创建一个工程,我命名的工程名为ReactiveCocoaDemo,

2.终端到工程路径下

cd ReactiveCocoaDemo/
pod search ReactiveCocoa

3.配置依赖文件

vi Podfile
    platform :ios,'5.0'
    pod 'ReactiveCocoa'
wq

4.下载文件

pod install /update

5:打开"Build Phases" 选中相应的target,添加 RAC 到 "Link Binary With Libraries".加上libReactiveCocoa-iOS.a

如下图

6:在Header Search Paths 中添加$(BUILD_ROOT)/../IntermediateBuildFilesPath/UninstalledProducts/include"

7:在 "Other Linker Flags" 添加 -ObjC

8:在ReactiveCocoaDemo-Prefix.pch 将RAC的头文件加进去

#import "ReactiveCocoa.h"

两个输入框 一个提示的Lable 一个按钮,1.当两个输入框的内容相同时 提示文字显示"成功" 按钮可以点击2.当任意一个输入框没有输入内容时 提示文字显示"请输入" 按钮不可点击3.当输入框非空且两个输入内容不同时 提示"请重新输入" 按钮不可点击

下面是部分实现代码:

@weakify(self);
[[RACObserve(self,warningText)
  filter:^(NSString *newString) {
      self.resultLabel.text = newString;
      return YES;
//          return [newString hasPrefix:@"Success"];
  }]
 subscribeNext:^(NSString *newString) {
     @strongify(self);
     self.bt.enabled = [newString hasPrefix:@"Success"];
 }];


RAC(self,self.warningText) = [RACSignal combineLatest:@[
RACObserve(self,self.input.text),RACObserve(self,self.verifyInput.text)]
reduce:^(NSString *password,NSString *passwordConfirm)
{
    if ([passwordConfirm isEqualToString:password])
    {
        return @"Success";
    }
    else if([password length] == 0 || [passwordConfirm length] ==0 )
    {
        return @"Please Input";
    }
    else
    return @"Input Error";
}
  ];

对应关系如下图所示

学过C++ 的应该觉得这个很类似于Qt中的信号槽机制

需要说明的是 因为RAC很大程度上是依赖于Block的.所以在RAC前面我们加上@weakify(my_variable) 避免循环引用,然后在每一个RAC块中为了防止提前释放我们需要用@strongify(my_variable)来对对象进行持有.详情点击

运行效果图如下

The [-subscribe…][RACSignal] methods give you access to the current and future values in a signal:

RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

// Outputs: A B C D
[letters subscribeNext:^(NSString *x) {
    NSLog(@"%@",x);
}];

For a cold signal,side effects will be performed once per subscription:

__block unsigned subscriptions = 0;

RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
    subscriptions++;
    [subscriber sendCompleted];
    return nil;
}];

// Outputs:
// subscription 1
[loggingSignal subscribeCompleted:^{
    NSLog(@"subscription %u",subscriptions);
}];

// Outputs:
// subscription 2
[loggingSignal subscribeCompleted:^{
    NSLog(@"subscription %u",subscriptions);
}];

This behavior can be changed using a [connection][Connections].

The [-do…][RACSignal+Operations] methods add side effects to a signal without actuallysubscribing to it:

__block unsigned subscriptions = 0;

RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
    subscriptions++;
    [subscriber sendCompleted];
    return nil;
}];

// Does not output anything yet
loggingSignal = [loggingSignal doCompleted:^{
    NSLog(@"about to complete subscription %u",subscriptions);
}];

// Outputs:
// about to complete subscription 1
// subscription 1
[loggingSignal subscribeCompleted:^{
    NSLog(@"subscription %u",subscriptions);
}];

These operators transform a single stream into a new stream.

The [-map:][RACStream] method is used to transform the values in a stream,andcreate a new stream with the results:

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;

// Contains: AA BB CC DD EE FF GG HH II
RACSequence *mapped = [letters map:^(NSString *value) {
    return [value stringByAppendingString:value];
}];

The [-filter:][RACStream] method uses a block to test each value,including itinto the resulting stream only if the test passes:

RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: 2 4 6 8
RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) {
    return (value.intValue % 2) == 0;
}];

These operators combine multiple streams into a single new stream.

The [-concat:][RACStream] method appends one stream's values to another:

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *concatenated = [letters concat:numbers];

The [-flatten][RACStream] operator is applied to a stream-of-streams,andcombines their values into a single new stream.

Sequences are concatenated:

RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *sequenceOfSequences = @[ letters,numbers ].rac_sequence;

// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
RACSequence *flattened = [sequenceOfSequences flatten];

Signals are merged:

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
    [subscriber sendNext:letters];
    [subscriber sendNext:numbers];
    [subscriber sendCompleted];
    return nil;
}];

RACSignal *flattened = [signalOfSignals flatten];

// Outputs: A 1 B C 2
[flattened subscribeNext:^(NSString *x) {
    NSLog(@"%@",x);
}];

[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];

Flattening isn't that interesting on its own,but understandinghow it works is important for [-flattenMap:][RACStream].

-flattenMap: is used to transform each of a stream's values into a newstream. Then,all of the streams returned will be flattened down into a singlestream. In other words,it's -map: followed by -flatten.

This can be used to extend or edit sequences:

RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
RACSequence *extended = [numbers flattenMap:^(NSString *num) {
    return @[ num,num ].rac_sequence;
}];

// Contains: 1_ 3_ 5_ 7_ 9_
RACSequence *edited = [numbers flattenMap:^(NSString *num) {
    if (num.intValue % 2 == 0) {
        return [RACSequence empty];
    } else {
        NSString *newNum = [num stringByAppendingString:@"_"];
        return [RACSequence return:newNum]; 
    }
}];

Or create multiple signals of work which are automatically recombined:

RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

[[letters
    flattenMap:^(NSString *letter) {
        return [database saveEntriesForLetter:letter];
    }]
    subscribeCompleted:^{
        NSLog(@"All database entries saved successfully.");
    }];

These operators combine multiple signals into a single new [RACSignal][].

[-then:][RACSignal+Operations] starts the original signal,waits for it to complete,and then only forwards the values from a new signal:

RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

// The new signal only contains: 1 2 3 4 5 6 7 8 9
//
// But when subscribed to,it also outputs: A B C D E F G H I
RACSignal *sequenced = [[letters
    doNext:^(NSString *letter) {
        NSLog(@"%@",letter);
    }]
    then:^{
        return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal;
    }];

This is most useful for executing all the side effects of one signal,thenstarting another,and only returning the second signal's values.

The [+merge:][RACSignal+Operations] method will forward the values from manysignals into a single stream,as soon as those values arrive:

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *merged = [RACSignal merge:@[ letters,numbers ]];

// Outputs: A 1 B C 2
[merged subscribeNext:^(NSString *x) {
    NSLog(@"%@",x);
}];

[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];

The [+combineLatest:][RACSignal+Operations] and +combineLatest:reduce: methodswill watch multiple signals for changes,and then send the latest values fromall of them when a change occurs:

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *combined = [RACSignal
    combineLatest:@[ letters,numbers ]
    reduce:^(NSString *letter,NSString *number) {
        return [letter stringByAppendingString:number];
    }];

// Outputs: B1 B2 C2 C3
[combined subscribeNext:^(id x) {
    NSLog(@"%@",x);
}];

[letters sendNext:@"A"];
[letters sendNext:@"B"];
[numbers sendNext:@"1"];
[numbers sendNext:@"2"];
[letters sendNext:@"C"];
[numbers sendNext:@"3"];

Note that the combined signal will only send its first value when all of theinputs have sent at least one. In the example above,@"A" was neverforwarded because numbers had not sent a value yet.

The [-switchToLatest][RACSignal+Operations] operator is applied toa signal-of-signals,and always forwards the values from the latest signal:

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSubject *signalOfSignals = [RACSubject subject];

RACSignal *switched = [signalOfSignals switchToLatest];

// Outputs: A B 1 D
[switched subscribeNext:^(NSString *x) {
    NSLog(@"%@",x);
}];

[signalOfSignals sendNext:letters];
[letters sendNext:@"A"];
[letters sendNext:@"B"];

[signalOfSignals sendNext:numbers];
[letters sendNext:@"C"];
[numbers sendNext:@"1"];

[signalOfSignals sendNext:letters];
[numbers sendNext:@"2"];
[letters sendNext:@"D"];
W 本站文章如果没有特殊说明,均为原创,转载请以链接方式注明本文地址: http://hufeng825.github.com/2013/10/13/ios31/

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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