本人有若干成套學(xué)習(xí)視頻, 可試看! 可試看! 可試看, 重要的事情說三遍 包含Java
, 數(shù)據(jù)結(jié)構(gòu)與算法
, iOS
, 安卓
, python
, flutter
等等, 如有需要, 聯(lián)系微信tsaievan
.
RAC - ReactiveCocoa, 是github上的一套開源框架, 是一套函數(shù)響應(yīng)式編程框架.
那么這套框架是干什么用的呢?
RAC中很重要的一個類叫做RACsignal
, 當(dāng)我們有數(shù)據(jù)產(chǎn)生時, 就創(chuàng)建一個信號, 這么說可能太抽象了
RACSignal
在創(chuàng)建對象的時候, 不是用的alloc
,init
方法, 而是給我們提供了一個類方法:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
在這個類方法中, 有一個參數(shù)didSubscribe
, 這是一個block
, 這個block
是一個參數(shù)為id
類型, 遵守RACSubscriber
協(xié)議的對象, 返回值為RACDisposable *
類型的block
.
第28行報(bào)錯, 這是因?yàn)槟愕?code>block是一個帶返回值的block
, 而你又什么都沒有返回, 所以不管三七二十一, 先返回一個nil
. 然后我在block
中做一件事情, 打印一句話, 代碼如下:
- (void)viewDidLoad {
[super viewDidLoad];
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"signal has been created");
return nil;
}];
}
代碼寫到這里, 這個block
中的代碼會執(zhí)行嗎? 顯然不會, 那么, 這段代碼什么時候執(zhí)行呢?
這個時候就需要信號訂閱:
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
這段代碼中, 又有一個block
參數(shù), 這個block
是一個參數(shù)為id
類型的對象, 返回值為空的block
, 信號訂閱時, 我在這個block
中什么也不做, 上面的打印同樣能夠執(zhí)行:
我們來看一下這個內(nèi)部是怎么實(shí)現(xiàn)的:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
return [RACDynamicSignal createSignal:didSubscribe];
}
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
其中signal->_didSubscribe = [didSubscribe copy];
這句代碼將didSubscribe
這個block
保存在對象的屬性中.
那我們來看是不是在- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
這個方法中調(diào)用了block
:
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
在subscribe
方法中,didSubscribe
方法被調(diào)用了
從上圖我們可以看到, didSubscribe
這個block
在信號訂閱的時候被調(diào)用, 在調(diào)用的同時, 把subscriber
這個參數(shù)傳給block
, 所以我們在
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"signal has been created");
[subscriber sendNext:@"subscriber send"];
return nil;
}];
這個代碼塊內(nèi)部是可以拿到subscriber
這個對象的, 然后我再調(diào)用sendNext
這個方法, 會出現(xiàn)什么情況呢?
我們發(fā)現(xiàn), subscribeNext:
方法參數(shù)的block
被調(diào)用了
那我們來看看sendNext
這個方法內(nèi)部做了什么?
這個方法執(zhí)行了我當(dāng)初保存在subscriber
屬性中的block
, 并且將value
參數(shù)也傳給了這個nextBlock
, 所以在nextBlock
執(zhí)行的時候, 可以獲得x參數(shù)
好了, 我們一直沒有關(guān)注+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
這個方法中block
參數(shù)的返回值是干嘛的, 那么我們就給這個block
一個返回值:
可以看到的是: 這個返回值是RACDisposable
類型的, 然后我們創(chuàng)建一個RACDisposable
對象是用+ (instancetype)disposableWithBlock:(void (^)(void))block
這個類方法, 同樣參數(shù)也是一個block
, 但是這個block
很快就被調(diào)用了, 這是為什么呢?
這個返回值
RACDisposable *disposable = [RACDisposable disposableWithBlock:^{
NSLog(@"disposable");
}];
return disposable;
在subscribeNext:
方法調(diào)用時, 賦值給了subscriber
的disposable
屬性(可以先這么理解), 當(dāng)subscriber
生存周期結(jié)束時, 就會調(diào)
- (void)dealloc {
[self.disposable dispose];
}
這時候
RACDisposable *disposable = [RACDisposable disposableWithBlock:^{
NSLog(@"disposable");
}];
return disposable;
這段代碼中的打印就會執(zhí)行了.
但當(dāng)subscriber
被強(qiáng)引用時, dealloc
方法不被調(diào)用, disposable
也不會被調(diào)用.
如果subscriber
被強(qiáng)引用時, 我又想取消訂閱, 應(yīng)該怎么做呢?
subscribeNext:
這個方法是有返回值的, 這個返回值其實(shí)就是上面didSubscribe
block的返回值(可以先這么理解)
我用一個變量接收一樣, 然后手動disposable
即可:
那么RACDisposable
是做什么用的呢?
這個類可以幫助我們?nèi)∠嗛? 當(dāng)信號發(fā)送完畢了或者失敗了之后取消訂閱, 并調(diào)用指定的代碼.
在RAC中, 還有一個類非常重要, 也很特殊, 叫做RACSubject, 這個類即可以創(chuàng)建信號, 也可以發(fā)送信號. 代碼如下:
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"subject has been subscribed %@", x);
}];
[subject sendNext:@"subject has been sended"];
- 在創(chuàng)建信號的時候: 這個框架重寫了
init
方法:
+ (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)建了一個可變數(shù)組. 這個可變數(shù)組是用來添加
subscriber
的
- (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;
}
- 然后在發(fā)送信號的時候, 會遍歷這個數(shù)組, 把里面的
subscriber
一一取出來, 分別發(fā)送信號:
所以, 用RACSubject
的好處就是: 可以訂閱多個信號, 最后發(fā)送信號的時候一起調(diào)用.
現(xiàn)在總結(jié)一下RACSignal和RACSubject
RACSubject的本質(zhì)其實(shí)和RACSignal是一樣的, 那為什么它又可以創(chuàng)建信號, 又可以訂閱信號呢?
在OC中, 是沒有多繼承的概念的, 要想實(shí)現(xiàn)多繼承的效果, 可以先繼承一個類, 然后再實(shí)現(xiàn)一個或者多個協(xié)議, 這樣就實(shí)現(xiàn)了多繼承的效果
那么, 說了這么多, 這個RAC有什么用呢?
用處大了, 因?yàn)樗梢詫?shí)現(xiàn)KVO , 代理, 通知, block的功能, 集多種功能于一身, 當(dāng)你用了RAC之后, 你可以利用RAC實(shí)現(xiàn)以上設(shè)計(jì)模式能實(shí)現(xiàn)的功能.
比如現(xiàn)在有一個簡單的需求:
一個自定義的view上面有一個button, 點(diǎn)擊button之后把button的背景顏色傳給控制器的view. 這是典型的逆向傳值, 通知, 代理, block都能用. 那用RAC怎么做呢?
- 首先是創(chuàng)建信號, 信號的創(chuàng)建需要在什么地方呢, 值從哪里傳, 信號創(chuàng)建就在哪里, 用一個懶加載創(chuàng)建subject對象:
- (RACSubject *)subject {
if (!_subject) {
_subject = [RACSubject subject];
}
return _subject;
}
- 然后在button點(diǎn)擊的時候?qū)㈩伾盘杺鞒鋈?
- (IBAction)buttonClick:(UIButton *)sender {
[self.subject sendNext:sender.backgroundColor];
}
- 最后, 值往哪里傳, 哪里就訂閱信號:
- (void)viewDidLoad {
[super viewDidLoad];
[self.yfView.subject subscribeNext:^(id _Nullable x) {
self.view.backgroundColor = x;
}];
}
就這么簡單的幾步, 就完成了需求, 非常簡單方便, 效果如下:
好城丧, 現(xiàn)在看另外一個重要的類测暗, 叫做RACReplaySubject
RACReplaySubject
是繼承自RACSubject
的
所以訂閱信號里的block
是可以執(zhí)行的. 那么這個類和RACSubject
有什么不同之處呢?
當(dāng)我們將訂閱信號和發(fā)送信號交換一下位置, 發(fā)現(xiàn)訂閱的block
依然可以執(zhí)行, 這是跟RACSubject
不同的地方. 但這是為什么呢?
這個只是初始RAC, 我也在從0開始學(xué)習(xí), 有錯誤的地方, 還請大家批評指正,共同提高.
首先來看創(chuàng)建RACReplaySubject
對象的時候, 有什么不一樣
可以看出的是, 這個類重寫了init
方法, 創(chuàng)建了一個名為valuesRecieved
的數(shù)組.
然后, 在發(fā)送信號的過程中:
在發(fā)送信號的時候, 剛才創(chuàng)建的那個valuesReceived
數(shù)組就把需要發(fā)送的信號保存在數(shù)組里. 保存在數(shù)組中干嘛呢? 且看:
從上圖可以看出, 在訂閱信號的時候, 會先將valuesReceived
數(shù)組中保存的信號來一波遍歷, 然后取出來, 自己發(fā)送掉. 這樣, 就算先發(fā)送信號, 在訂閱信號, 也可以完成, 因?yàn)橛嗛喰盘柕膬?nèi)部自己也發(fā)送了信號.