上次討論了如何讓數(shù)據(jù)全局同步棕洋,但是在同步到UI層的時(shí)候還是有些麻煩《锾蓿現(xiàn)在來解決UI層的問題把沼。
之前的方案有兩種:
- 在viewWillAppear的時(shí)候,reloadData秩伞,缺點(diǎn)是如果需要reload的數(shù)據(jù)太多逞带,大量計(jì)算會(huì)導(dǎo)致阻塞主線程,雖然可能沒有那么嚴(yán)重纱新,但是有些時(shí)候還是能夠感知出來掰担。
- 使用KVO來監(jiān)聽變化,缺點(diǎn)是代碼侵入性太強(qiáng)怒炸,而且嚴(yán)重影響了一些代碼的統(tǒng)一性带饱。
下面是使用KVO的一個(gè)例子:
@weakify(self);
[self.KVOController observe:_record keyPath:NSStringFromSelector(@selector(praiseCount)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, MZRecord *object, NSDictionary<NSString *,id> * _Nonnull change) {
@strongify(self);
self.recordTabbar.praiseCount = object.praiseCount;
}];
[self.KVOController observe:_record keyPath:NSStringFromSelector(@selector(praised)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, MZRecord *object, NSDictionary<NSString *,id> * _Nonnull change) {
@strongify(self);
[self.recordTabbar setPraised:object.isPraised animated:self.view.window != nil];
}];
[self.KVOController observe:_record keyPath:NSStringFromSelector(@selector(collected)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, MZRecord *object, NSDictionary<NSString *,id> * _Nonnull change) {
@strongify(self);
[self.recordTabbar setCollected:object.collected animated:self.view.window != nil];
}];
[self.KVOController observe:_record keyPath:NSStringFromSelector(@selector(collectCount)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, MZRecord *object, NSDictionary<NSString *,id> * _Nonnull change) {
@strongify(self);
self.recordTabbar.collectCount = object.collectCount;
}];
[self.KVOController observe:_record keyPath:NSStringFromSelector(@selector(commentCount)) options:NSKeyValueObservingOptionNew block:^(id _Nullable observer, MZRecord *object, NSDictionary<NSString *,id> * _Nonnull change) {
@strongify(self);
self.recordTabbar.commentCount = object.commentCount;
}];
上面是我們?yōu)榱送?個(gè)按鈕的狀態(tài)的代碼,我們使用了一個(gè)第三方庫(kù)來簡(jiǎn)化KVO的編寫阅羹,但還是非常的冗余勺疼。
為此,開始思考有什么更簡(jiǎn)單的方法捏鱼。
開始我們想要封裝KVO执庐,直接綁定數(shù)據(jù)和UI,但是很多數(shù)據(jù)并不是一一對(duì)應(yīng)的导梆,比如數(shù)字轨淌,狀態(tài)迂烁,是需要轉(zhuǎn)化的,而且狀態(tài)變更很多情況下是需要?jiǎng)有У牡蒺模詿o論如何都不免不了監(jiān)聽和轉(zhuǎn)換這兩個(gè)東西盟步。
后來想,既然數(shù)據(jù)可以做全局同步躏结,那么是否可以把視圖也看作一種類型的資源却盘,也自動(dòng)同步該狀態(tài)屬性呢?按照這種思路媳拴,將視圖改寫支持這種方式來同步黄橘。
@interface UIView (MZChannel) <MZChannelProtocol>
// 我們需要在創(chuàng)建的時(shí)候就確定類型,而且不能修改屈溉,防止意料之外的情況
- (instancetype)initWithFrame:(CGRect)frame channelType:(NSInteger)channelType;
// 加入數(shù)據(jù)池中塞关,并且內(nèi)部增加了lock,保證線程安全
- (void)bindId:(NSString *)id;
// 為了避免與view自身屬性沖突子巾,增加了一個(gè)白名單配置
- (NSArray<NSString *> *)channelWhiteList;
@end
查看一下我們修改之后的狀態(tài)
// RecordTabbar
// 新增這兩個(gè)方法帆赢,由于之前設(shè)計(jì)中的接口與該keyPath統(tǒng)一,所以其他內(nèi)容不需要修改
- (NSInteger)channelType {
return MZResourceTypeNote;
}
- (NSArray<NSString *> *)channelWhiteList {
return @[NSStringFromSelector(@selector(praised)),
NSStringFromSelector(@selector(praiseCount)),
NSStringFromSelector(@selector(collected)),
NSStringFromSelector(@selector(collectCount)),
NSStringFromSelector(@selector(commentCount))];
}
// 部分對(duì)應(yīng)的setter方法
- (void)setPraised:(BOOL)praised {
_praised = praised;
[self.praiseButton setPraised:praised animated:self.window != nil];
}
- (void)setPraised:(BOOL)praised animated:(BOOL)animated {
_praised = praised;
[self.praiseButton setPraised:praised animated:animated];
}
- (void)setPraiseCount:(NSInteger)praiseCount {
_praiseCount = praiseCount;
self.praiseButton.praiseCount = praiseCount;
}
- (void)setCollected:(BOOL)collected {
[self setCollected:collected animated:self.window != nil];
}
由于該頁(yè)面資源id并不會(huì)變化砰左,所以只需要在初始化的時(shí)候綁定一次id就可以了匿醒。
[self.recordTabbar bindId:self.record.id];
這樣我們的后半部分流程(從數(shù)據(jù)到顯示)也完整了场航,整個(gè)流程都依賴于MZChannel進(jìn)行缠导。