需求
在搜索中輸入任何文字,立即聯想到相關的搜索關鍵詞,以列表的方式進行顯示
最簡單的解決方案步驟:
- 監(jiān)聽輸入框文字的變化.
- 在回調中發(fā)起網絡請求
- 將請求的結果顯示出來
問題
- 在用戶輸入比較快的情況下,前面幾個請求只是在浪費用戶的流量,因為請求的結果會立即被覆蓋
- 由于網絡的不確定性,可能后請求的接口要晚于早請求的結果得到返回結果
使用ReactiveCocoa
針對問題1. 解決的辦法是限流:
/// 問題1解決
self.throttleSubject = [RACSubject subject];
[[self.throttleSubject throttle:1] subscribeNext:^(id x) {
[self requestWithKeyWord:x];
}];
/// 模擬搜索框輸入
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"1"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"2"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"3"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"4"];
});
打印的結果是3,4
原理:在限流的時間內,如果沒有新的數據過來,時間到了,就會sendNext
數據,否則,釋放上個計時器(計時器也是個信號,通過dispose進行無效化),重新設置一個新的計時器倒計時
針對問題2.
/// 問題2解決
self.requestSignal = [RACSubject subject];
[self.requestSignal.switchToLatest subscribeNext:^(id x) {
NSLog(@"請求結果 : %@",x);
}];
原理:switchToLatest
這個信號內部會在收到新的信號時候,將上一個信號進行釋放,也就是說在A,B兩個網絡請求信號,按順序請求的話,如果在B請求前,B請求已經結束,那么,沒有任何問題,這時候搜索的內容和關鍵字肯定還是匹配的,如果B請求的時候,A還沒有sendNext
|sendComplete
那么,會將A信號進行dispose
掉,這樣即使A信號得到數據了也會return掉
- (void)sendNext:(id)value {
if (self.disposable.disposed) return;
.......
}
其實這個時候比較好的網絡請求代碼如下
- (RACSignal *)requestWithKeyWord:(NSString *)keyword {
if (keyword.length == 0) return nil;
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
AFHTTPSessionManager *manager;
NSURLSessionDataTask *task = [manager POST:nil parameters:nil progress:nil success:nil failure:nil];
return [RACDisposable disposableWithBlock:^{
/// 此處可以中斷網絡請求
[task cancel];
}];
}];
}
這樣會使得信號在被新的網絡請求信號沖刷掉的時候,及時終止網絡請求,節(jié)省資源.
完整代碼
- (void)testThrottle {
/// 問題2解決
self.requestSignal = [RACSubject subject];
[self.requestSignal.switchToLatest subscribeNext:^(id x) {
NSLog(@"請求結果 : %@",x);
}];
/// 問題1解決
self.throttleSubject = [RACSubject subject];
[[self.throttleSubject throttle:1] subscribeNext:^(id x) {
NSLog(@"keyword : %@",x);
[self.requestSignal sendNext:[self requestWithKeyWord:x]];
}];
/// 模擬搜索框輸入
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"1"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"2"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"3"];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.throttleSubject sendNext:@"4"];
});
}
- (RACSignal *)requestWithKeyWord:(NSString *)keyword {
if (keyword.length == 0) return nil;
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:keyword];
});
return nil;
}];
}
keyword : 3
keyword : 4
請求結果 : 4