RACSignal
RAC中統(tǒng)一的數(shù)據(jù)接口,控件的事件杜漠,包括KVO勤家,timer都可以轉(zhuǎn)化成RACSignal抵卫。
創(chuàng)建:
1.RAC未控件的一部分原來的事件都通過Category的方式定義了event對應的signal,只需要直接拿來使用就好了抒巢。
2.自己創(chuàng)建
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))
didSubscribe;
? 3.訂閱
[self.usernameTextField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
在信號創(chuàng)建的時候贫贝,需要傳入一個didSubscribe的block,在有其他訂閱者訂閱這個信號的時候,didSubscribe就會被調(diào)用稚晚,然后將數(shù)據(jù)通過subscribeNext的block傳入崇堵。
信號事件的種類
1.next,一般情況下客燕,信號處理業(yè)務邏輯正常返回的時候鸳劳,會調(diào)用訂閱者的sendNext方法將數(shù)據(jù)傳入訂閱者,訂閱者可以通過過subscribeNext獲取數(shù)據(jù)也搓。
2.error赏廓,有時候業(yè)務邏輯產(chǎn)生異常的時候,會調(diào)用訂閱者的sendError方法來告知訂閱者產(chǎn)生了異常傍妒,訂閱者在subscribeNext:erro:中處理異常錯誤幔摸。
3.completed,該事件表示訂閱者從信號中移除颤练,之后不再收到消息了既忆,信號生命周期結(jié)束。
創(chuàng)建一個請求賬號權(quán)限的信號
- (RACSignal *)requestAccessToTwitterSignal {
// 1 - define an error
NSError *accessError = [NSError errorWithDomain:RWTwitterInstantDomain
code:
RWTwitterInstantErrorAccessDenied
userInfo:nil];
// 2 - create the signal
@weakify(self)
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3 - request access to twitter
@strongify(self)
[self.accountStore
requestAccessToAccountsWithType:self.twitterAccountType
options:nil
completion:^(BOOL granted, NSError *error) {
// 4 - handle the response
if (!granted) {
[subscriber sendError:accessError];
} else {
[subscriber sendNext:nil];
[subscriber sendCompleted];
}
}];
return nil;
}];
}
處理請求賬號權(quán)限的信號
[[self requestAccessToTwitterSignal]
subscribeNext:^(id x) {
NSLog(@"Access granted");
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
信號的幾種操作
filter
將信號過濾嗦玖,只保留滿足block中條件的信號
只保留輸入字符串長度大于3的信號
[[self.usernameTextField.rac_textSignal
filter:^BOOL(id value) {
NSString *text = value;
return text.length > 3;
}]
subscribeNext:^(id x) {
//這里只會顯示長度大于3的字符串
NSLog(@"%@", x);
}];
這里的信號先經(jīng)過filter過濾了一次患雇,再由subscribeNext得到過濾的信號進行處理,其數(shù)據(jù)流如下圖所示:
map
把源信號的值映射成一個新的值
[[[self.usernameTextField.rac_textSignal
map:^id(NSString *text) {
return @(text.length);
}]
filter:^BOOL(NSNumber *length) {
return [length integerValue] > 3;
}]
subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
信號先經(jīng)過map映射將字符串轉(zhuǎn)化成功了字符串長度的數(shù)據(jù)宇挫,再通過filter過濾掉了字符串長度等于3的信號苛吱,最后通過subscribeNext獲得最后符合條件的信號,整個過程包含了不同數(shù)據(jù)類型的變化捞稿,過程如下圖:
http://7xqgnx.com1.z0.glb.clouddn.com/RAC2.png
combineLatest
將多個信號合并起來又谋,每一個被合并的信號必須調(diào)用過一次sendNext,才能觸發(fā)合并的信號娱局,最后以元組的形式發(fā)出彰亥。
reduce
當信號發(fā)出的內(nèi)容是元組時,可以使用它將元組聚合成一個值衰齐。
將對姓名和密碼的驗證結(jié)果的兩種信合合并成一個信號任斋,再通過reduce返回最終是否驗證通過的信號。
RACSignal *signUpActiveSignal =
[RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid
) {
return @([usernameValid boolValue] && [passwordValid
boolValue]);
}];
數(shù)據(jù)流的狀態(tài):
doNext
在每次信號執(zhí)行訂閱者的Next方法之前耻涛,會調(diào)用這個方法废酷,它對信號本身不會產(chǎn)生影響。
在按鈕點擊之后抹缕,會有一系列驗證的過程澈蟆,在這期間,按鈕不能被再次點擊卓研,等待驗證結(jié)果出來之后趴俘,在恢復按鈕未正常使用的狀態(tài)睹簇。
[[[[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];
}
}];
數(shù)據(jù)流的狀態(tài):
then
用于連接兩個信號,當?shù)谝粋€信號完成之后(前一個信號調(diào)用sendCompleted)寥闪,才會連接then返回的信號太惠,then之前的信號會被忽略
在得到否有權(quán)限獲得賬戶信息之后,通過then獲取搜索文本的信號疲憋,再驗證搜索文本的有效性凿渊,最后獲取有效的搜索文本。如果獲取賬號信息這一步出現(xiàn)錯誤缚柳,直接調(diào)用最后的error的block埃脏。
[[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
filter:^BOOL(NSString *text) {
@strongify(self)
return [self isValidSearchText:text];
}]
subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
數(shù)據(jù)流如下圖:
deliverOn
信號傳遞切換到指定線程中
現(xiàn)在創(chuàng)建一個信號在后臺線程下載一個圖片,可以考慮使用SDWebImage
-(RACSignal *)signalForLoadingImage:(NSString *)imageUrl {
RACScheduler *scheduler = [RACScheduler
schedulerWithPriority:RACSchedulerPriorityBackground];
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber
) {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]
];
UIImage *image = [UIImage imageWithData:data];
[subscriber sendNext:image];
[subscriber sendCompleted];
return nil;
}] subscribeOn:scheduler];
}
圖片下載完成之后切換到主線程秋忙,設置圖片的顯示
cell.twitterAvatarView.image = nil;
[[[self signalForLoadingImage:tweet.profileImageUrl]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(UIImage *image) {
cell.twitterAvatarView.image = image;
}];
throttle
節(jié)流剂癌,可以設置某一段時間內(nèi)不發(fā)送信號,等過了這段時間翰绊,將最新的信號發(fā)出
在用戶輸入搜索文字的時候,每一次textChange都會觸發(fā)搜索結(jié)果的網(wǎng)絡請求旁壮。請求次數(shù)過于頻繁且沒有必要监嗜,因為有時候可能是用戶還沒有輸完而已。并且結(jié)果頻繁的顯示清空體驗也不好÷招常現(xiàn)在通過throttle處理每經(jīng)過0.5秒處理一次搜索請求裁奇。
[[[[[[[self requestAccessToTwitterSignal]
then:^RACSignal *{
@strongify(self)
return self.searchText.rac_textSignal;
}]
filter:^BOOL(NSString *text) {
@strongify(self)
return [self isValidSearchText:text];
}]
throttle:0.5]
flattenMap:^RACStream *(NSString *text) {
@strongify(self)
return [self signalForSearchWithText:text];
}]
deliverOn:[RACScheduler mainThreadScheduler]]
subscribeNext:^(NSDictionary *jsonSearchResult) {
NSArray *statuses = jsonSearchResult[@"statuses"];
NSArray *tweets = [statuses linq_select:^id(id tweet) {
return [RWTweet tweetWithStatus:tweet];
}];
[self.resultsViewController displayTweets:tweets];
} error:^(NSError *error) {
NSLog(@"An error occurred: %@", error);
}];
經(jīng)過最后的處理,數(shù)據(jù)流如下圖所示:
在requestAccessToTwitterSignal和signalForSearchWithText都有可能產(chǎn)生error麦撵,最后都會直接調(diào)用subscribleNext:error方法刽肠。
takeUnitl
(RACSignal *) 獲取信號直到某個信號執(zhí)行完成
比如,監(jiān)聽某個文本框的文本改變直到當前對象被銷毀
[_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];
skip
(NSUInteger) 跳過幾個信號免胃,選擇忽略不處理
第一次輸入不被監(jiān)聽
[[_textField.rac_textSignal skip:1] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
常見的宏
RAC(object, property)
RAC(_titleLabel, text) = [viewModel.titleSignal takeUntil:self.
rac_prepareForReuseSignal];
表示將一個對象的屬性和一個signal進行綁定音五,signal每產(chǎn)生一個value,都會自動執(zhí)行如下代碼
[TARGET setValue:value ?: NIL_VALUE forKeyPath:KEYPATH];
RACObserve(TARGET, KEYPATH)
返回一個signal羔沙,檢測target的keypath屬性
RAC和RACObserve用在一起實現(xiàn)雙向綁定
RAC(self.outputLabel, text) = RACObserve(self.model, name);