在開(kāi)始MVVM之前,RAC是肯定繞不過(guò)去的。MVVM+RAC真的是雙劍合璧。今天 我們先來(lái)看看RAC的使用和一些原理實(shí)現(xiàn),然后再結(jié)合項(xiàng)目中的登錄來(lái)實(shí)踐一下。
RACSignal
先從最簡(jiǎn)單的RACSignal開(kāi)始,我們先來(lái)看看它是怎么創(chuàng)建的。
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self creatSigal];
}
-(void)creatSigal{
RACSignal *sigal=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"你個(gè)小樣"];
return nil;
}];
[sigal subscribeNext:^(id _Nullable x) {
NSLog(@"傳遞的數(shù)據(jù)是:----%@",x);
}];
}
輸出打印的是:LBDaySurgery(Dev)[10211:3806511] 傳遞的數(shù)據(jù)是:----你個(gè)小樣
上面就創(chuàng)建了一個(gè)信號(hào),并且訂閱信號(hào),還發(fā)送消息了。創(chuàng)建是成功了,但是它具體的原來(lái)還不知道啊。不要慌,我們點(diǎn)進(jìn)方法看看。
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
創(chuàng)建信號(hào)的方法是這么個(gè)東西。首先RACDynamicSignal這是個(gè)什么,然后再點(diǎn)發(fā)現(xiàn)
@interface RACDynamicSignal : RACSignal
它其實(shí)就是一個(gè)信號(hào),先初始化了RACDynamicSignal,然后下面_didSubscribe這個(gè)熟悉嘛,是不是有點(diǎn)像訂閱信號(hào),RACDynamicSignal持有了didSubscribe,最后返回了一個(gè)信號(hào)。這個(gè)方法看完了,有一個(gè)疑問(wèn),didSubscribe這個(gè)是用來(lái)干嘛的?接著往下看
訂閱信號(hào)的方法
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
第一行很容易理解,就是創(chuàng)建RACSubscriber(訂閱者),點(diǎn)進(jìn)去看看它里面的實(shí)現(xiàn)
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
RACSubscriber *subscriber = [[self alloc] init];
subscriber->_next = [
copy];
subscriber->_error = [error copy];
subscriber->_completed = [completed copy];
return subscriber;
}
我們發(fā)現(xiàn),它初始化了訂閱者,然后訂閱者持有了next這個(gè)block,持有了error、completed,很簡(jiǎn)單。點(diǎn)進(jìn)去看[self subscribe:o];的實(shí)現(xiàn)
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
//看的有點(diǎn)懵,上面的對(duì)象都不知道是什么 但是didSubscribe這個(gè)熟悉啊,在創(chuàng)建信號(hào)的時(shí)候RACDynamicSignal持有didSubscribe
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
//這里調(diào)用didSubscribe方法,并且把剛才傳入的subscriber調(diào)用出去
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
return disposable;
}
現(xiàn)在我們最開(kāi)始的疑問(wèn)解開(kāi)了,didSubscribe的調(diào)用。這個(gè)方法里能看懂的就是,先判斷有沒(méi)有didSubscribe,有的話就執(zhí)行這個(gè)方法,參數(shù)是subscriber訂閱者。后面我們?cè)谡f(shuō)RACDisposable。
- (void)sendNext:(id)value {
@synchronized (self) {
void (^nextBlock)(id) = [self.next copy];
if (nextBlock == nil) return;
nextBlock(value);
}
}
上面是訂閱者發(fā)送消息的方法實(shí)現(xiàn),判斷有沒(méi)有nextblock,而這里的nextblock也正是subscriber->_next = [next copy];保存的。如果有 就把發(fā)送的消息,傳遞出去。
上面我們一個(gè)一個(gè)的都點(diǎn)進(jìn)去看了他們的具體實(shí)現(xiàn),現(xiàn)在來(lái)張圖更加直觀的看看這個(gè)過(guò)程

這里面有要說(shuō)明的是:didSubscribe的參數(shù)是RACSubscriber,而在我們發(fā)送消息的時(shí)候, [subscriber sendNext:@"你個(gè)小樣"]; 這里的訂閱者就是didSubscribe傳入的,而訂閱者持有nextblock,所以nextBlock(value);消息就傳入到訂閱的block中了。這就是創(chuàng)建RACSignal的一個(gè)流程。
RACDisposable
在上面創(chuàng)建信號(hào)的時(shí)候,返回了空,但是我們點(diǎn)進(jìn)方法里面,它應(yīng)該返回一個(gè)RACDisposable。
1.什么是RACDisposable
RACDisposable:用于取消訂閱或者清理資源,當(dāng)信號(hào)銷(xiāo)毀或者發(fā)送錯(cuò)誤的時(shí)候,就會(huì)自動(dòng)觸發(fā)。
使用場(chǎng)景:不想監(jiān)聽(tīng)某個(gè)信號(hào)時(shí),可以通過(guò)它主動(dòng)取消訂閱信號(hào)
我們來(lái)看看它的使用:
-(void)creatSigal{
RACSignal *sigal=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"創(chuàng)建成功");
[subscriber sendNext:@"你個(gè)小樣"];
NSLog(@"發(fā)送消息之后");
return [RACDisposable disposableWithBlock:^{
NSLog(@"這是什么什么時(shí)候調(diào)用");
}];
}];
[sigal subscribeNext:^(id _Nullable x) {
NSLog(@"傳遞的數(shù)據(jù)是:----%@",x);
}];
}
打印出:2021-04-08 14:08:52.687740+0800 LBDaySurgery(Dev)[10407:3878985] 創(chuàng)建成功
2021-04-08 14:08:52.687781+0800 LBDaySurgery(Dev)[10407:3878985] 傳遞的數(shù)據(jù)是:----你個(gè)小樣
2021-04-08 14:08:52.687795+0800 LBDaySurgery(Dev)[10407:3878985] 發(fā)送消息之后
2021-04-08 14:08:52.687819+0800 LBDaySurgery(Dev)[10407:3878985] 這是什么什么時(shí)候調(diào)用
我們發(fā)現(xiàn)信號(hào)被銷(xiāo)毀以后,就就會(huì)調(diào)用。
另外我們還可以主動(dòng)調(diào)用 也會(huì)銷(xiāo)毀
RACDisposable *disposable=[sigal subscribeNext:^(id _Nullable x) {
NSLog(@"傳遞的數(shù)據(jù)是:----%@",x);
}];
[disposable dispose];
RACSubject
創(chuàng)建信號(hào)當(dāng)然不是一種方法,還有其他的方法。先上代碼
-(void)creatracSubject{
RACSubject *subject=[RACSubject subject];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"收到的消息是:---%@",x);
}];
//發(fā)送消息
[subject sendNext:@"666 666"];
}
LBDaySurgery(Dev)[10505:3899054] 收到的消息是:---666 666
我們點(diǎn)進(jìn)方法看看它的實(shí)現(xiàn)原理
+ (instancetype)subject {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self == nil) return nil;
_disposable = [RACCompoundDisposable compoundDisposable];
_subscribers = [[NSMutableArray alloc] initWithCapacity:1];
return self;
}
創(chuàng)建subject對(duì)象時(shí),初始化了一個(gè)取消信號(hào)和一個(gè)數(shù)組,從名字我們能看出這是用來(lái)存放訂閱者的。
接著看訂閱信號(hào)的實(shí)現(xiàn)
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
好熟悉,和RACSignal一毛一樣,但是里面里面的實(shí)現(xiàn)就不同了,而且他兩也不是同一個(gè)類(lèi)。創(chuàng)建RACSubscriber *o 這個(gè)是一樣的我們不需要看,主要是下面的[self subscribe:o];
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
NSMutableArray *subscribers = self.subscribers;
@synchronized (subscribers) {
[subscribers addObject:subscriber];
}
[disposable addDisposable:[RACDisposable disposableWithBlock:^{
@synchronized (subscribers) {
// Since newer subscribers are generally shorter-lived, search
// starting from the end of the list.
NSUInteger index = [subscribers indexOfObjectWithOptions:NSEnumerationReverse passingTest:^ BOOL (id<RACSubscriber> obj, NSUInteger index, BOOL *stop) {
return obj == subscriber;
}];
if (index != NSNotFound) [subscribers removeObjectAtIndex:index];
}
}]];
return disposable;
}
從上面這段代碼能看到,是把訂閱者都加入到subscribers數(shù)組中了。
我們?cè)倏窗l(fā)布消息的實(shí)現(xiàn)
- (void)enumerateSubscribersUsingBlock:(void (^)(id<RACSubscriber> subscriber))block {
NSArray *subscribers;
@synchronized (self.subscribers) {
subscribers = [self.subscribers copy];
}
//遍歷subscribers中的訂閱者,然后block傳遞subscriber
for (id<RACSubscriber> subscriber in subscribers) {
block(subscriber);
}
}
#pragma mark RACSubscriber
- (void)sendNext:(id)value {
[self enumerateSubscribersUsingBlock:^(id<RACSubscriber> subscriber) {
//這里和之前RACSignal發(fā)送消息相同
[subscriber sendNext:value];
}];
}
1.創(chuàng)建的subject內(nèi)部會(huì)創(chuàng)建數(shù)組_subscribers,用來(lái)保存所有的訂閱者。
2.訂閱信息的時(shí)候會(huì)創(chuàng)建訂閱者
3.發(fā)送消息的時(shí)候,會(huì)依次發(fā)送。
-(void)creatracSubject{
RACSubject *subject=[RACSubject subject];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"收到的消息是:---%@",x);
}];
//發(fā)送消息
[subject sendNext:@"666 666"];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"第二次收到:--%@",x);
}];
[subject sendNext:@"888888"];
}
上面這個(gè)會(huì)打印出什么呢?在第一次發(fā)送消息的時(shí)候[subject sendNext:@"666 666"];,會(huì)打印出666,當(dāng)調(diào)用到[subject sendNext:@"888888"];,因?yàn)閟ubject可以多次訂閱,所以不僅第二次會(huì)打印,第一次同樣也會(huì)打印出。所以最后打印出
2021-04-08 15:38:37.497503+0800 LBDaySurgery(Dev)[10525:3913448] 收到的消息是:---666 666
2021-04-08 15:38:37.497542+0800 LBDaySurgery(Dev)[10525:3913448] 收到的消息是:---888888
2021-04-08 15:38:37.497555+0800 LBDaySurgery(Dev)[10525:3913448] 第二次收到:--888888
我們?cè)倏聪旅孢@段代碼
-(void)creatracSubject{
RACSubject *subject=[RACSubject subject];
[subject sendNext:@"888888"];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"收到的消息是:---%@",x);
}];
}
一運(yùn)行發(fā)現(xiàn)沒(méi)打印出來(lái),這是為什么呢?這是因?yàn)樵诎l(fā)送消息是要遍歷存放訂閱者的數(shù)組,但是這個(gè)數(shù)組現(xiàn)在是空的,它只有在訂閱消息的時(shí)候才會(huì)加入到數(shù)組,自然就不會(huì)發(fā)送消息,也就不會(huì)打印出結(jié)果。
如果非要把順序?qū)懗上劝l(fā)送消息 在訂閱,那么我們可以用RACReplaySubject來(lái)實(shí)現(xiàn)
-(void)creatReplaySubject{
RACReplaySubject *subject=[RACReplaySubject subject];
[subject sendNext:@"888888"];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"收到的消息是:---%@",x);
}];
}