高大上函數響應式編程框架ReactiveCocoa學習筆記1 簡介

原創文章,轉載請聲明出處哈。

ReactiveCocoa函數響應式編程


1、簡介
ReactiveCocoa(其簡稱爲RAC)是函數響應式編程框架。RAC具備函數式編程和響應式編程的特性。它主要吸收了.Net的 Reactive Extensions的設計和實現。


函數式編程 (Functional Programming)
函數式編程也能夠寫N篇,它是徹底不一樣於OO的編程模式,這裏主要講一下這個框架使用到的函數式思想。
1) 高階函數:在函數式編程中,把函數當參數來回傳遞,而這個,說成術語,咱們把他叫作高階函數。在oc中,blocks是被普遍使用的參數傳遞,它其實是匿名函數。  
     高階函數調用過程有點像linux命令裏的pipeline(管道),一個命令調用後的輸出看成另外一個命令輸入,多個命令之間能夠串起來操做。來個例子:linux

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與普通的函數式編程類似,可是每一個函數能夠接收一個輸入值的流,若是其中,一個新的輸入值到達的話,這個函數將根據最新的輸入值從新計算,而且產生一個新的輸出。這是一種」數據流"編程模式。


2、爲何咱們要用它
1,開發過程當中,狀態以及狀態之間依賴過多,RAC更加有效率地處理事件流,而無需顯式去管理狀態。在OO或者過程式編程中,狀態變化是最難跟蹤,最頭痛的事。這個也是最重要的一點。
2,減小變量的使用,因爲它跟蹤狀態和值的變化,所以不須要再申明變量不斷地觀察狀態和更新值。
3,提供統一的消息傳遞機制,將oc中的通知,action,KVO以及其它全部UIControl事件的變化都進行監控,當變化發生時,就會傳遞事件和值。
4,當值隨着事件變換時,可使用map,filter,reduce等函數便利地對值進行變換操做。


3、什麼時候使用


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的框架概覽,敬請期待哈。編程語言

相關文章
相關標籤/搜索