高大上函数响应式编程框架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" ];
    }];




第一小节未完成,第二节讲RAC的框架概览,敬请期待哈。

转载于:https://www.cnblogs.com/linjinxing1980/p/4180695.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值