高大上函数响应式编程框架ReactiveCocoa学习笔记1 简介

原创文章,转载请声明出处哈。

ReactiveCocoa函数响应式编程

一、简介

ReactiveCocoa(其简称为RAC)是函数响应式编程框架。RAC具有函数式编程和响应式编程的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。

函数式编程 (Functional Programming)

函数式编程也可以写N篇,它是完全不同于OO的编程模式,这里主要讲一下这个框架使用到的函数式思想。

1) 高阶函数:在函数式编程中,把函数当参数来回传递,而这个,说成术语,我们把他叫做高阶函数。在oc中,blocks是被广泛使用的参数传递,它实际上是匿名函数。

高阶函数调用过程有点像linux命令里的pipeline(管道),一个命令调用后的输出当作另一个命令输入,多个命令之间可以串起来操作。来个例子:


1

2

3

4

5

6

7

8

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

 

   
//
Contains: 22 44 66 88

   
RACSequence
*doubleNumber = [[numbers filter:^
BOOL (NSString *value)
{

   
   
return (value.intValue
% 2) == 0;

   
}]

 
  map:^
id(id value)
{

   
   
return [value
stringByAppendingString:value];

   
}];

上面的例子是数组里的值先进行过滤filter,得到偶数,然后再将结果的每个值进行map操作,调用stringByAppendingString,最终输出22 44 66 88.

2) 惰性(或延迟)求值:Sequences对象等,只有当被使用到时,才会对其求值。

关于函数编程,有兴趣的大家可以研究下haskell或者clojure,不过目前好多语言都在借用函数式的思想。

响应式编程(Functional Reactive Programming:FRP)

响应式编程是一种和事件流有关的编程模式,关注导致状态值改变的行为事件,一系列事件组成了事件流。

一系列事件是导致属性值发生变化的原因。FRP非常类似于设计模式里的观察者模式。

响应式编程是一种针对数据流和变化传递的编程模式,其执行引擎可以自动的在数据流之间传递数据的变化。比如说,在一种命令式编程语言中,a: = b + c 表示 a 是 b + c 表达式的值,但是在RP语言中,它可能意味着一个动态的数据流关系:当c或者b的值发生变化时,a的值自动的发生变化。

RP已经被证实是一种最有效的处理交互式用户界面、实时模式下的动画的开发模式,但本质上是一种基本的编程模式。现在最为热门的JavaFX脚本语言中,引入的bind就是RP的一个概念实现。

响应式编程其关键点包括:

1) 输入被视为"行为",或者说一个随时间而变化的事件流

2) 连续的、随时间而变化的值

3) 按时间排序的离散事件序列

FRP与普通的函数式编程相似,但是每个函数可以接收一个输入值的流,如果其中,一个新的输入值到达的话,这个函数将根据最新的输入值重新计算,并且产生一个新的输出。这是一种”数据流"编程模式。

二、为什么我们要用它

1,开发过程中,状态以及状态之间依赖过多,RAC更加有效率地处理事件流,而无需显式去管理状态。在OO或者过程式编程中,状态变化是最难跟踪,最头痛的事。这个也是最重要的一点。

2,减少变量的使用,由于它跟踪状态和值的变化,因此不需要再申明变量不断地观察状态和更新值。

3,提供统一的消息传递机制,将oc中的通知,action,KVO以及其它所有UIControl事件的变化都进行监控,当变化发生时,就会传递事件和值。

4,当值随着事件变换时,可以使用map,filter,reduce等函数便利地对值进行变换操作。

三、何时使用

1,处理异步或者事件驱动的数据变化


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

static void *ObservationContext
= &ObservationContext;

-
(
void)viewDidLoad
{

   
[super viewDidLoad];

   
[LoginManager.sharedManager
addObserver:
self forKeyPath:@"loggingIn" options:NSKeyValueObservingOptionInitial context:&ObservationContext];

   
[NSNotificationCenter.defaultCenter
addObserver:
self selector:@selector(loggedOut:)
name:UserDidLogOutNotification object:LoginManager.sharedManager];

   
[self.usernameTextField
addTarget:
self action:@selector(updateLogInButton)
forControlEvents:UIControlEventEditingChanged];

   
[self.passwordTextField
addTarget:
self action:@selector(updateLogInButton)
forControlEvents:UIControlEventEditingChanged];

   
[self.logInButton
addTarget:
self action:@selector(logInPressed:)
forControlEvents:UIControlEventTouchUpInside];

}

-
(
void)dealloc
{

   
[LoginManager.sharedManager
removeObserver:
self forKeyPath:@"loggingIn" context:ObservationContext];

   
[NSNotificationCenter.defaultCenter
removeObserver:
self];

}

-
(
void)updateLogInButton
{

   
BOOL textFieldsNonEmpty
=
self.usernameTextField.text.length
> 0 &&
self.passwordTextField.text.length
> 0;

   
BOOL readyToLogIn
= !LoginManager.sharedManager.isLoggingIn && !
self.loggedIn;

   
self.logInButton.enabled
= textFieldsNonEmpty && readyToLogIn;

}

-
(
IBAction)logInPressed:(UIButton
*)sender {

   
[[LoginManager
sharedManager]

   
   
logInWithUsername:self.usernameTextField.text

   
   
password:self.passwordTextField.text

   
   
success:^{

   
       
self.loggedIn
=
YES;

   
   
}
failure:^(
NSError *error)
{

   
       
[self presentError:error];

   
   
}];

}

-
(
void)loggedOut:(NSNotification *)notification
{

   
self.loggedIn
=
NO;

}

-
(
void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(
id)object
change:(
NSDictionary *)change
context:(
void *)context
{

   
if (context
== ObservationContext) {

   
   
[self updateLogInButton];

   
}
else {

   
   
[super observeValueForKeyPath:keyPath
ofObject:object change:change context:context];

   
}

}

//
RAC实现:

-
(
void)viewDidLoad
{

   
[super viewDidLoad];

   
@weakify(self);

   
RAC(self.logInButton,
enabled) = [RACSignal

   
   
combineLatest:@[

   
       
self.usernameTextField.rac_textSignal,

   
       
self.passwordTextField.rac_textSignal,

   
       
RACObserve(LoginManager.sharedManager,
loggingIn),

   
       
RACObserve(self,
loggedIn)

   
   
]
reduce:^(
NSString *username,
NSString *password,
NSNumber *loggingIn,
NSNumber *loggedIn)
{

   
       
return @(username.length
> 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue);

   
   
}];

   
[[self.logInButton
rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) {

   
   
@strongify(self);

   
   
RACSignal
*loginSignal = [LoginManager.sharedManager

   
       
logInWithUsername:self.usernameTextField.text

   
       
password:self.passwordTextField.text];

   
       
[loginSignal
subscribeError:^(
NSError *error)
{

   
           
@strongify(self);

   
           
[self presentError:error];

   
       
}
completed:^{

   
           
@strongify(self);

   
           
self.loggedIn
=
YES;

   
       
}];

   
}];

   
RAC(self,
loggedIn) = [[
NSNotificationCenter.defaultCenter

   
   
rac_addObserverForName:UserDidLogOutNotification
object:
nil]

   
   
mapReplace:@NO];

}

2, 链式的依赖操作


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

[client
logInWithSuccess:^{

   
[client
loadCachedMessagesWithSuccess:^(
NSArray *messages)
{

   
   
[client
fetchMessagesAfterMessage:messages.lastObject success:^(
NSArray *nextMessages)
{

   
       
NSLog(@"Fetched
all messages."
);

   
   
}
failure:^(
NSError *error)
{

   
       
[self presentError:error];

   
   
}];

   
}
failure:^(
NSError *error)
{

   
   
[self presentError:error];

   
}];

}
failure:^(
NSError *error)
{

   
[self presentError:error];

}];

 //
     RAC实现:

[[[[client
logIn]

   
then:^{

   
   
return [client
loadCachedMessages];

   
}]

   
flattenMap:^(NSArray *messages)
{

   
   
return [client
fetchMessagesAfterMessage:messages.lastObject];

   
}]

   
subscribeError:^(NSError *error)
{

   
   
[self presentError:error];

   
}
completed:^{

   
   
NSLog(@"Fetched
all messages."
);

   
}];

3, 并行依赖操作:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

__block
NSArray *databaseObjects;

__block
NSArray *fileContents;

NSOperationQueue *backgroundQueue
= [[
NSOperationQueue alloc]
init];

NSBlockOperation *databaseOperation
= [
NSBlockOperation blockOperationWithBlock:^{

   
databaseObjects
= [databaseClient fetchObjectsMatchingPredicate:predicate];

}];

NSBlockOperation *filesOperation
= [
NSBlockOperation blockOperationWithBlock:^{

   
NSMutableArray *filesInProgress
= [
NSMutableArray array];

   
for (NSString *path
in files) {

   
   
[filesInProgress
addObject:[
NSData dataWithContentsOfFile:path]];

   
}

   
fileContents
= [filesInProgress
copy];

}];

NSBlockOperation *finishOperation
= [
NSBlockOperation blockOperationWithBlock:^{

   
[self finishProcessingDatabaseObjects:databaseObjects
fileContents:fileContents];

   
NSLog(@"Done
processing"
);

}];

[finishOperation
addDependency:databaseOperation];

[finishOperation
addDependency:filesOperation];

[backgroundQueue
addOperation:databaseOperation];

[backgroundQueue
addOperation:filesOperation];

[backgroundQueue
addOperation:finishOperation];

//
RAC 实现

RACSignal
*databaseSignal = [[databaseClient

   
fetchObjectsMatchingPredicate:predicate]

   
subscribeOn:[RACScheduler
scheduler]];

RACSignal
*fileSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(
id<RACSubscriber>
subscriber) {

   
NSMutableArray *filesInProgress
= [
NSMutableArray array];

   
for (NSString *path
in files) {

   
   
[filesInProgress
addObject:[
NSData dataWithContentsOfFile:path]];

   
}

   
[subscriber
sendNext:[filesInProgress
copy]];

   
[subscriber
sendCompleted];

}];

[[RACSignal

   
combineLatest:@[
databaseSignal, fileSignal ]

   
reduce:^
id (NSArray *databaseObjects,
NSArray *fileContents)
{

   
   
[self finishProcessingDatabaseObjects:databaseObjects
fileContents:fileContents];

   
   
return nil;

   
}]

   
subscribeCompleted:^{

   
   
NSLog(@"Done
processing"
);

   
}];

4, 简化集合操作


1

2

3

4

5

6

7

8

9

10

NSMutableArray *results
= [
NSMutableArray array];

for (NSString *str
in strings) {

   
if (str.length
< 2) {

   
   
continue;

   
}

   
NSString *newString
= [str stringByAppendingString:@
"foobar"];

   
[results
addObject:newString];

}

//RAC实现:


1

2

3

4

5

6

7

RACSequence
*results = [[strings.rac_sequence

   
filter:^
BOOL (NSString *str)
{

   
   
return str.length
>= 2;

   
}]

   
map:^(NSString *str)
{

   
   
return [str
stringByAppendingString:@
"foobar"];

   
}];

时间: 2024-11-08 19:13:55

高大上函数响应式编程框架ReactiveCocoa学习笔记1 简介的相关文章

响应式编程框架ReactiveCocoa学习——框架概览

这篇博客将会继续翻译RAC的官方文档Framework Overview. 主要是对RAC这和框架进行概览的介绍和学习.同时也可以参考我前面的两篇翻译<响应式编程框架ReactiveCocoa学习--基本操作符><响应式编程框架ReactiveCocoa介绍与入门>.其中ReactiveCocoa的Github官方地址为 https://github.com/ReactiveCocoa/ReactiveCocoa . 这篇文档包括了RAC中的对不同组件的高层描述,并解释如何进行结合

IOS响应式编程框架ReactiveCocoa(RAC)使用示例

ReactiveCocoa是响应式编程(FRP)在IOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# :在网上看了几篇文章,感觉理论讲了很多,但是代码还是看不太懂,于是自己把它github文档上的一些使用的经典示例实现了一下,项目中有需要时可以直接搬过去用,用的熟练了再读源码也比较容易理解. 例1. 监听对象的成员变量变化,当成员变量值被改变时,触发做一些事情. 这种情况其实就是IOS KVO机制使用的场景,使用

IOS响应式编程框架ReactiveCocoa(RAC)使用示例-备

ReactiveCocoa是响应式编程(FRP)在IOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# :在网上看了几篇文章,感觉理论讲了很多,但是代码还是看不太懂,于是自己把它github文档上的一些使用的经典示例实现了一下,项目中有需要时可以直接搬过去用,用的熟练了再读源码也比较容易理解. 例1. 监听对象的成员变量变化,当成员变量值被改变时,触发做一些事情. 这种情况其实就是IOS KVO机制使用的场景,使用

函数响应式编程及ReactiveObjC学习笔记 (-)

最近无意间看到一个视频讲的ReactiveObjC, 觉得挺好用的 但听完后只是了解个大概. 在网上找了些文章, 有的写的比较易懂但看完还是没觉得自己能比较好的使用RAC, 有的甚至让我看不下去 这两天刚好公司项目交付闲下来, 想自己去啃下官方文档 ReactiveCocoa是一个基于函数响应式编程的OC框架. 那么什么是函数式响应式编程呢?概念我就不讲了 因为我讲的也不一定准确, 大家可以去baidu看看大神们的解释 下面我大概演示下响应式编程的样子 Masonry是比较常见的一个响应式框架,

iOS响应式编程框架ReactiveCocoa讲解与实例演示

Signal and Subscriber 这是RAC最核心的内容,如果用插头和插座来描述,插座是Signal,插头是Subscriber.插座负责去获取电,插头负责使用电,而且一个插座可以插任意数量的插头.当一个插座(Signal)没有插头 (Subscriber)时什么也不干,也就是处于冷(Cold)的状态,只有插了插头时才会去获取,这个时候就处于热(Hot)的状态. Signal获取到数据后,会调用Subscriber的sendNext, sendComplete, sendError方法

函数响应式编程及ReactiveObjC学习笔记 (二)

之前我们初步认识了RAC的设计思路跟实现方式, 现在我们再来看看如果使用它以及它能帮我们做什么 One of the major advantages of RAC is that it provides a single, unified approach to dealing with asynchronous behaviors, including delegate methods, callback blocks, target-action mechanisms, notificat

web多终端开发学习系列(一)--- 响应式布局框架BootStrap学习

最近在温习web的开发,毕业之后就没接触web开发了.当时HTML5在国内貌似还是刚刚起步,能适配HTML5的浏览器很少.移动界面框架也才刚刚开始,记得当时曾经学过sencha touch的移动框架以及jquery mobile框架.这两个框架都对响应式布局进行了不错的适配及实现.响应式布局说白了就是对于多个移动终端只需一个界面即可全部适配,可大大地减少开发的工作量. 最近一年Bootstrap开始广泛地被使用了,基于此框架开发出来的插件数不胜数.所以我花了几天时间学习了下Bootstrap,本

[iOS] 响应式编程开发-ReactiveCocoa(二)

RAC实现图片下载功能 在实现异步RAC下载图片的过程中,需要注意以下几点: • 通过 NSURLConnection 对象的 +(RACSignal *)rac_sendAsynchronousRequest:(NSURLRequest*)r 可以创建网络异步下载的信号量对象: • 在执行完成异步下载过程后,需要通过 [x deliverOn:[RACScheduler mainThreadScheduler]] 操作将方法返回到主线程进行执行: @implementation ViewCon

Swift 响应式编程 浅析

这里我讲一下响应式编程(Reactive Programming)是如何将异步编程推到一个全新高度的. 异步编程真的很难 大多数有关响应式编程的演讲和文章都是在展示Reactive框架如何好如何惊人,给出一些在非常复杂的情况下,只需几行代码就可以搞定的例子.例子么?我这里有一段基于RxSwift的聊天程序的代码: socket.rx_event .filter({ $0.event == "newMessage" && $0.items?.count > 0})