Context
反復接觸 ReactiveCocoa
,這次真的準備把它應用到實際開發(fā)中了.為了以后使用方便,這里列出一些常用關鍵字的使用方法,以備查詢.
常用方法
簡單訂閱 subscribeNext
使用場景:
“ 如果你改變了,讓我知道 “
[self.usernameTextField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
過濾條件 filter
使用場景:
“ 如果你改變了,并且滿足x條件, 那么再讓我知道 “
[[self.usernameTextField.rac_textSignal
filter:^BOOL(id value) {
NSString* text = value;
return text.length > 4;
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
還有拆分的寫法,因為 block層級太深 ,可讀性不好:
RACSignal* usernameSourceSignal = self.usernameTextField.rac_textSignal;
RACSignal* filteredUsernameSignal =
[usernameSourceSignal filter:^BOOL(id value) {
NSString* text = value;
return text.length > 3;
}];
[filteredUsernameSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
類型轉換 map
使用場景:
當需要從輸入信號中提取不同的信息時(比如這里, 從打印下個字符,到打印長度)
[[[self.usernameTextField.rac_textSignal
map:^id(NSString* value) {
return @(value.length);
}] filter:^BOOL(NSNumber* value) {
return [value integerValue] > 4;
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
- 注意: 可 map 的只能是對象
RAC 宏
使用場景: RAC(A,b)
利用信號改變 A 的 b 屬性值
// 驗證信號
RACSignal *validUsernameSignal =
[self.usernameTextField.rac_textSignal
map:^id(NSString *text) {
return @([self isValidUsername:text]);
}];
RAC(self.usernameTextField, backgroundColor) =
[validUsernameSignal
map:^id(NSNumber *passwordValid){
return[passwordValid boolValue] ? [UIColor clearColor]:[UIColor yellowColor];
}];
聚合信號 combineLatest
使用場景:
多個信號條件同時滿足, 才能產生有效信號(比如登陸的時候,用戶名有效并且密碼有效的時候,登陸按鈕才應該有效)
RACSignal *signUpActiveSignal =
[
RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid){
return @([usernameValid boolValue]&&[passwordValid boolValue]);
}
];
- 使用combineLatest:reduce:方法把validUsernameSignal和validPasswordSignal產生的最新的值聚合在一起舔亭,并生成一個新的信號斜棚。每次這兩個源信號的任何一個產生新值時奇钞,reduce block都會執(zhí)行,block的返回值會發(fā)給下一個信號商叹。
事件信號
使用場景:
拿到UIKit控件的事件響應信號
[[self.signInButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(id x) {
NSLog(@"button clicked");
}];
封裝方法
使用場景:
想把一個異步的API 封裝成信號
- (RACSignal *)signInSignal {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
[self.signInService
signInWithUsername:self.usernameTextField.text
password:self.passwordTextField.text
complete:^(BOOL success){
[subscriber sendNext:@(success)];
[subscriber sendCompleted];
}];
return nil;
}];
}
拿到信號中的信號 flattenMap
使用場景:
當需要從包含信號b的信號a中拿取信號b
[[[self.signInButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
flattenMap:^id(id x){
return[self signInSignal];
}]
subscribeNext:^(id x){
NSLog(@"Sign in result: %@", x);
}];
添加附加操作(Adding side-effects)
使用場景:
需要進行一些準備工作的時候
[[[[self.signInButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
doNext:^(id x){
self.signInButton.enabled =NO;
self.signInFailureText.hidden =YES;
}]
flattenMap:^id(id x){
return[self signInSignal];
}]
subscribeNext:^(NSNumber*signedIn){
self.signInButton.enabled =YES;
BOOL success =[signedIn boolValue];
self.signInFailureText.hidden = success;
if(success){
[self performSegueWithIdentifier:@"signInSuccess" sender:self];
}
}];
移除訂閱 dispose
使用場景:
當需要手動釋放一個信號(當沒有訂閱,信號就不復存在),但是使用場景很少,僅供了解
RACSignal *backgroundColorSignal =
[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}];
RACDisposable *subscription =
[backgroundColorSignal
subscribeNext:^(UIColor *color) {
self.searchText.backgroundColor = color;
}];
// at some point in the future ...
[subscription dispose];
防止循環(huán)引用
@weakify(self)
[[self.searchText.rac_textSignal
map:^id(NSString *text) {
return [self isValidSearchText:text] ?
[UIColor whiteColor] : [UIColor yellowColor];
}]
subscribeNext:^(UIColor *color) {
@strongify(self)
self.searchText.backgroundColor = color;
}];
next error completed
在signal的生命周期中眯搭,它可能不發(fā)送事件结窘,發(fā)送一個或多個next事
件,在這之后還能發(fā)送一個completed事件或一個error事件失乾。
[[self requestAccessToTwitterSignal]
subscribeNext:^(id x) {
NSLog(@"Access granted");
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
信號鏈接 then
使用場景:
當后面的信號需要依賴前面的信號時
[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
then方法會等待completed事件的發(fā)送常熙,然后再訂閱由then block返回的signal。這樣就高效地把控制權從一個signal傳遞給下一個仗扬。
then方法會跳過error事件症概,因此最終的subscribeNext:error: block還是會收到獲取訪問權限那一步發(fā)送的error事件。
異步信號
使用場景:
后臺加載資源
-(RACSignal *)signalForLoadingImage:(NSString *)imageUrl {
RACScheduler *scheduler = [RACScheduler
schedulerWithPriority:RACSchedulerPriorityBackground];
return [[RACSignal createSignal:^RACDisposable *(id subscriber) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];
UIImage *image = [UIImage imageWithData:data];
[subscriber sendNext:image];
[subscriber sendCompleted];
return nil;
}] subscribeOn:scheduler];
}
- subscribeOn:來確保signal在指定的scheduler上執(zhí)行早芭。
在主線程上更新UI
cell.twitterAvatarView.image = nil;
[[[self signalForLoadingImage:tweet.profileImageUrl]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(UIImage *image) {
cell.twitterAvatarView.image = image;
}];
針對cell的重用問題, 有種更優(yōu)化的方法:
[[[[self signalForLoadingImage:tweet.profileImageUrl]
takeUntil:cell.rac_prepareForReuseSignal]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(UIImage *image) {
cell.twitterAvatarView.image = image;
}];
延時響應 throttle
使用場景:
當用戶輸入完畢, 自定進行搜索的時候,不應該用戶每次改變輸入,都馬上搜索,應該當用戶停止輸入 x 秒之后,再進行搜索
@weakify(self);
[[self.passwordTextField.rac_textSignal throttle:2] subscribeNext:^(id x) {
@strongify(self);
self.hintLabel.text = (NSString*)x;
}];
參考網址:
http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1
http://benbeng.leanote.com/post/ReactiveCocoaTutorial-part1