需求
在搜索中輸入任何文字,立即聯(lián)想到相關(guān)的搜索關(guān)鍵詞,以列表的方式進(jìn)行顯示
最簡(jiǎn)單的解決方案步驟:
- 監(jiān)聽輸入框文字的變化.
- 在回調(diào)中發(fā)起網(wǎng)絡(luò)請(qǐng)求
- 將請(qǐng)求的結(jié)果顯示出來
問題
- 在用戶輸入比較快的情況下,前面幾個(gè)請(qǐng)求只是在浪費(fèi)用戶的流量,因?yàn)檎?qǐng)求的結(jié)果會(huì)立即被覆蓋
- 由于網(wǎng)絡(luò)的不確定性,可能后請(qǐng)求的接口要晚于早請(qǐng)求的結(jié)果得到返回結(jié)果
使用ReactiveCocoa
針對(duì)問題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"];
});
打印的結(jié)果是3,4
原理:在限流的時(shí)間內(nèi),如果沒有新的數(shù)據(jù)過來,時(shí)間到了,就會(huì)sendNext數(shù)據(jù),否則,釋放上個(gè)計(jì)時(shí)器(計(jì)時(shí)器也是個(gè)信號(hào),通過dispose進(jìn)行無效化),重新設(shè)置一個(gè)新的計(jì)時(shí)器倒計(jì)時(shí)
針對(duì)問題2.
/// 問題2解決
self.requestSignal = [RACSubject subject];
[self.requestSignal.switchToLatest subscribeNext:^(id x) {
NSLog(@"請(qǐng)求結(jié)果 : %@",x);
}];
原理:switchToLatest這個(gè)信號(hào)內(nèi)部會(huì)在收到新的信號(hào)時(shí)候,將上一個(gè)信號(hào)進(jìn)行釋放,也就是說在A,B兩個(gè)網(wǎng)絡(luò)請(qǐng)求信號(hào),按順序請(qǐng)求的話,如果在B請(qǐng)求前,B請(qǐng)求已經(jīng)結(jié)束,那么,沒有任何問題,這時(shí)候搜索的內(nèi)容和關(guān)鍵字肯定還是匹配的,如果B請(qǐng)求的時(shí)候,A還沒有sendNext|sendComplete那么,會(huì)將A信號(hào)進(jìn)行dispose掉,這樣即使A信號(hào)得到數(shù)據(jù)了也會(huì)return掉
- (void)sendNext:(id)value {
if (self.disposable.disposed) return;
.......
}
其實(shí)這個(gè)時(shí)候比較好的網(wǎng)絡(luò)請(qǐng)求代碼如下
- (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:^{
/// 此處可以中斷網(wǎng)絡(luò)請(qǐng)求
[task cancel];
}];
}];
}
這樣會(huì)使得信號(hào)在被新的網(wǎng)絡(luò)請(qǐng)求信號(hào)沖刷掉的時(shí)候,及時(shí)終止網(wǎng)絡(luò)請(qǐng)求,節(jié)省資源.
完整代碼
- (void)testThrottle {
/// 問題2解決
self.requestSignal = [RACSubject subject];
[self.requestSignal.switchToLatest subscribeNext:^(id x) {
NSLog(@"請(qǐng)求結(jié)果 : %@",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
請(qǐng)求結(jié)果 : 4