RAC常見用法
1.代替代理
我們還是通過一個例子在了解一下吧
現在呢在我們的控制器中有一個我們自定義的View叫BlueView,里面有一個按鈕,點擊這個按鈕,要我們的控制器去處理事件。
以往呢我們基本就是通過 代理、通知、Block,那么我們用RAC來如何實現呢?
首先在我們的BlueView有一個按鈕的點擊事件處理方法。
-(IBAction)btnClick:(id)sender;
接下來在我們的控制器中
[[_blueView rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"%@",x);
}];
rac_signalForSelector 這個方法的作用就是 去監聽_blueView的哪個方法的調用,它返回給我們的就是一個信號,直接訂閱。這里給我們返回的 x 是一個集合,這個集合里面的數據 就是我們btnClick方法傳的參數了。
2.代替KVO
首先在這里我們要監聽_blueView的frame屬性值的變化,我們需要導入這個頭文件
#import <NSObject+RACKVOWrapper.h>
[_blueView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionOld |NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
//回調
NSLog(@"value%@---%@",value,change);
}];
在這里我們直接注冊監聽,通過Block 就直接回調了我們屬性發生變化的事件。這樣處理起來就使得我們代碼邏輯非常清晰了。
另一種寫法
[[_blueView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
3.監聽事件
那么在這里 我們在來看看通過RAC如何監聽我們的事件呢?
在我們的控制器中有一個按鈕“_btn”,那么我們要監聽按鈕的點擊事件。
之前我們一般是怎么做呢?是不是通過_btn的 addTarget:(nullable id) action:(nonnull SEL) forControlEvents:(UIControlEvents)方法,去添加一個事件,然后再去實現這個方法。
那么!來看看 RAC是實現方式
[[_btn rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"%@",x);
}];
對 沒錯 就是這么簡單粗暴。就可以在Block中 直接去處理你的 點擊事件了。這就是 函數式編程思想。
rac_signalForControlEvents 這個方法就是說 把左邊的按鈕的什么事件轉成一個信號,然后訂閱這個信號。
4.代替通知
在這里 我們以監聽一個鍵盤的通知為例,
先想想我們之前的 通知是怎么寫的。
是不是...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWasShow:)
name:UIKeyboardWillShowNotification
object:nil];
然后再去實現 keyboardWasShow 這個方法。
那么來看看 我們通過RAC是如何寫的
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"%@",x);
}];
是的 沒錯 就是這樣簡單 方便,相信這句話就不用解釋了吧!
5.監聽文本框
監聽文本框的輸入,實時拿到文本框輸入的值。
根據我們系統的方式是 通過實現文本框的協議,去獲取。
那么看看 RAC
[_textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
NSLog(@"%@",x);
}];
是的 x 就是你文本框輸入的值。
6.代替NSTimer
接下來呢,在補充一個東西
在我們以往使用NSTimer 做定時循環執行的時候,
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
大家有沒有遇到過,如果timerMethod正在執行,而此時如果有UI事件的觸發,比如滾動我們的屏幕,我們timerMethod執行將會被暫停執行,一旦UI事件執行完畢,timerMethod又會開始執行。原因是我們的NSTimer的事件是交給Runloop去處理,那么Runloop在執行的時候UI模式具有最高優先權。
那要解決這種問題怎么辦呢?大家可能會想到,把他放到子線程中去執行, 開啟runloop循環。
NSThread * thread = [[NSThread alloc]initWithBlock:^{
NSTimer * timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
//Runloop模式 && 多線程!!
//NSDefaultRunLoopMode 默認模式;
//UITrackingRunLoopMode UI模式:只能被UI事件喚醒!!
//NSRunLoopCommonModes 占位模式:默認&UI模式
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//開啟runloop循環
[[NSRunLoop currentRunLoop] run];
}];
[thread start];
還有一種 通過GCD設置timer
@interface ViewController ()
/** */
@property(nonatomic,strong)dispatch_source_t timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//GCD設置timer
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
//GCD的事件單位是納秒
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"-----_%@",[NSThread currentThread]);
});
//啟動
dispatch_resume(timer);
_timer = timer;
}
@end
這兩種方式呢都可以解決我們的問題。可是這樣是不是感覺比較麻煩呢?肯定有更簡單點兒的,那就是我們RAC。
@interface ViewController ()
@property(nonatomic,strong)RACDisposable * timerDisposable;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_timerDisposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"%@",[NSThread currentThread]);
}];
}
-(void)dealloc{
//取消訂閱!!
[_timerDisposable dispose];
}
@end