全局?jǐn)?shù)據(jù)同步(三)終極方案

在全局?jǐn)?shù)據(jù)同步系列文章中(一)(二)分別解決了model和view的全局同步座舍,但是依然有一些問題拳球,所以在這里給一個終極解決方案DDKeyPathChannel蛛勉。

重新來說明下解決的問題

由于各種原因剔桨,目前有兩個表示同一種類型的model限匣。

@interface UserModel1 : NSObject

@property (strong, nonatomic) NSString *id;
@property (strong, nonatomic) NSString *name;
@property (assign, nonatomic) NSInteger age;

@end

@interface UserModel2 : NSObject

@property (strong, nonatomic) NSString *id;
@property (strong, nonatomic) NSString *nickName;
@property (assign, nonatomic) NSInteger age;

@end

現(xiàn)在需要其中一個屬性修改了讥珍,或者第三方要求更新屬性历极,如何更好的同步各個不同model之間的屬性呢?

另一個問題衷佃,又如何把這個狀態(tài)更新到UI上呢趟卸?

以前方案的問題

首先,來看看之前解決方案的幾個問題纲酗。

  • 所有基類都需要實現(xiàn)特定接口協(xié)議衰腌。這對于model來說會比較簡單,但是對于UIView來說就比較麻煩觅赊。
  • 使用上右蕊,實現(xiàn)上比較麻煩,需要注意的地方比較多吮螺,容易犯錯誤饶囚。

那么有沒有不影響到原來的類的方式呢?

新思路

既然現(xiàn)有的類去實現(xiàn)這個協(xié)議比較麻煩鸠补,那么找一個第三方類萝风,永久的實現(xiàn)這個接口,并且把消息轉(zhuǎn)發(fā)到現(xiàn)有的類不就可以了嗎紫岩。

我們都知道有一個類不繼承于NSObject规惰,功能就是代理,那么我們利用這個類來做消息轉(zhuǎn)發(fā)泉蝌。

接口如下

@interface DDKeyPathChannelBaseProxy : NSProxy <DDKeyPathChannelProtocol>

// 以下兩個屬性確定對象唯一性
@property (readonly, nonatomic) NSInteger channelType;
@property (readonly, nonatomic) NSString *channelId;

// 原本的對象
@property (weak, readonly, nonatomic) __kindof NSObject *target;

- (instancetype)initWithChannelType:(NSInteger)channelType channelId:(NSString *)channelId target:(NSObject *)target;

@end

// 這是一個通過keyPath+白名單的實現(xiàn)歇万,可以通過mapper來映射真正的keyPath
@interface DDKeyPathChannelProxy : DDKeyPathChannelBaseProxy

@property (strong, nonatomic) NSArray<NSString *> *whiteList;
@property (strong, nonatomic) NSDictionary<NSString *, NSString *> *keyPathMapper; // messageKeyPath : realKeyPath

@property (strong, nonatomic) void(^valueWillChangeBlock)(__kindof NSObject *target, NSString *keyPath, __kindof id newValue);
@property (strong, nonatomic) void(^valueDidChangeBlock)(__kindof NSObject *target, NSString *keyPath, __kindof id newValue);

@end

// 這是一個block的實現(xiàn)揩晴,可以在同步的時候自定義轉(zhuǎn)換與實現(xiàn)
@interface DDKeyPathBlockChannelProxy : DDKeyPathChannelBaseProxy

@property (strong, nonatomic) NSString *keyPath;

@property (strong, nonatomic) void(^valueChangedBlock)(__kindof NSObject *target, NSString *keyPath, __kindof id newValue);

@end

轉(zhuǎn)發(fā)的核心在于消息的傳遞

// keyPath
- (void)setValue:(id)value forKey:(NSString *)key {
    if (self.valueWillChangeBlock) self.valueWillChangeBlock(self.target, key, value);
    [self.target setValue:value forKey:key];
    if (self.valueDidChangeBlock) self.valueDidChangeBlock(self.target, key, value);
}

// block
- (void)setValue:(id)value forKey:(NSString *)key {
    if ([key isEqualToString:self.keyPath]) {
        if (self.valueChangedBlock) {
            self.valueChangedBlock(self.target, key, value);
        }
    }
}

那么我們怎么去掛載這個代理對象呢,想到associate object贪磺,那么我們也很容易的控制自己的生命周期了硫兰。

這樣,我們就不需要在現(xiàn)有類中實現(xiàn)方法來支持該功能了寒锚,而且這樣也更好的封裝屏蔽了這些比較特殊的功能劫映。在實踐中感覺這種方式的使用成本是最低的,大家也比較容易接受刹前。

[self.user1 bindChannelType:ChannelTypeUser
                  channelId:self.user1.id];
[self.user2 addChannelProxyWithChannelType:ChannelTypeUser
                                 channelId:self.user2.id
                                    config:^(DDKeyPathChannelProxy *proxy) {
                                        proxy.keyPathMapper = @{ @"name": @"nickName" };
}];
// 更新屬性
[[DDKeyPathChannelManager sharedChannel] emitChannelType:ChannelTypeUser
                                               channelId:@"1"
                                                   value:@"Tom"
                                              forKeyPath:@"name"];
[[DDKeyPathChannelManager sharedChannel] emitChannelType:ChannelTypeUser
                                               channelId:@"1"
                                                   value:@(30)
                                              forKeyPath:@"age"];

UI層更新也可以通過這種方式泳赋,也可以選擇使用KVO。

題外話

關(guān)于這個功能腮郊,很多人肯定想到了ReactiveX摹蘑,關(guān)于這點(diǎn),兩者的確有部分相似的場景轧飞,但也有很多不同的地方。

關(guān)于更新UI這點(diǎn)撒踪,兩者從效果上來看的確是一致的

object -> (signal, keyPath) -> UI

兩者最大的不同在于过咬,ReactiveX是monad的思想,是有輸入輸出制妄,擁有明確的輸入對象和觀察對象掸绞,行為流程是從上游到下游。而本套方案是一個中間人模式耕捞,是一個星狀結(jié)構(gòu)衔掸,更像通知一點(diǎn)。

但是兩者思想是類似的俺抽,ReactiveX是把各種行為封裝成Signal敞映,而我們是把消息使用keyPath來承載與轉(zhuǎn)發(fā)。

如果想要使用ReactiveX來實現(xiàn)這個功能也不是不可以磷斧,創(chuàng)建一個全局的熱型號(subject)振愿,控制好回收(dispose),也是可以實現(xiàn)該功能弛饭,但總感覺和RX的概念有點(diǎn)偏差了冕末。

總結(jié)

從第一篇方案,到現(xiàn)在最終比較完美的一套方案侣颂,也是因為我們的需求在一步一步的變化档桃,要求我們使用更好、更靈活的方案才能滿足的結(jié)果憔晒。這個過程是一個不斷思考不斷反思的過程藻肄,從這個方案的演化中蔑舞,我深有感悟,很多東西在創(chuàng)造出來的時候看似完美仅炊,但實際上還有很大的完善空間斗幼,同時別人的方案也會對自己的想法有很多的幫助。所以多了解別人的實現(xiàn)方案對自己的提升還是很有幫助的抚垄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蜕窿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子呆馁,更是在濱河造成了極大的恐慌桐经,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浙滤,死亡現(xiàn)場離奇詭異阴挣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)纺腊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門畔咧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揖膜,你說我怎么就攤上這事誓沸。” “怎么了壹粟?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵拜隧,是天一觀的道長。 經(jīng)常有香客問我趁仙,道長洪添,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任雀费,我火速辦了婚禮干奢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘坐儿。我一直安慰自己律胀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布貌矿。 她就那樣靜靜地躺著炭菌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪逛漫。 梳的紋絲不亂的頭發(fā)上黑低,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼克握。 笑死蕾管,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的菩暗。 我是一名探鬼主播掰曾,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼停团!你這毒婦竟也來了旷坦?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤佑稠,失蹤者是張志新(化名)和其女友劉穎秒梅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舌胶,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捆蜀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了幔嫂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辆它。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖履恩,靈堂內(nèi)的尸體忽然破棺而出娩井,到底是詐尸還是另有隱情,我是刑警寧澤似袁,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站咐刨,受9級特大地震影響昙衅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜定鸟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一而涉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧联予,春花似錦啼县、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卷胯,卻和暖如春子刮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工挺峡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留葵孤,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓橱赠,卻偏偏與公主長得像尤仍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子狭姨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容