iOS項目架構

來源:互聯網
上載者:User

標籤:

最近看了一些有關server的東西,一些很簡單的東西,不外乎是一些文檔規範,另外結合最近看的wwdc的一些video,覺得對軟體架構(software architecture)認識又清楚了一些,這裡記錄下來。

software architecture 聽上去是一個很大的概念,實際上也包括很多東西,裡面的爭議也很多。在我看來軟體架構最好放在小的情境中理解。

問題1

我們有2個頁面。

 

 

 頁面A:首頁面

· 頁面B:詳情頁面

demo code 1.0.0

2個頁面分別顯示一個數字,這個數字應該相同。詳情會修改這個數字,這裡我們發現,詳情頁面和首頁面數字不一樣

資料不一致

問題1 解決方案A

這裡首先的感覺就是,詳情頁面返回,首頁面資料沒有重新整理,導致資料不一致。 那麼Fix這個Bug的方法,就是在首頁面出現的時候重新整理介面

12345 - (void)viewWillAppear:(BOOL)animated {    [super viewWillAppear:animated];     self.displayLabel.text = [[CUDataDAO selectData].data stringValue];}

現在來看,還不錯。但是,我們調用selectData的次數則變得非常非常多。資料不是經常變化的。

demo code 1.0.1

問題1 解決方案B

我們發現既然資料的改變是在頁面B進行的,那麼頁面B修改這個資料的時候,應該把資料變化”通知”給頁面A,那麼我們寫了一個Delegate

12345 @protocol CUDetailViewControllerDelegate <NSObject> - (void)detailVC:(CUDetailViewController *)vc dataChanged:(NSNumber *)data; @end

在頁面B修改資料之後,通過delegate 通知給頁面A。

12345678910 - (IBAction)changeButtonClicked:(id)sender {    int value = arc4random() % 100;    [CUDataDAO setData:value];     self.displayLabel.text = [@(value) stringValue];     if ([self.delegate respondsToSelector:@selector(detailVC:dataChanged:)]) {        [self.delegate detailVC:self dataChanged:@(value)];    }}

到此情境1得到了不錯的解決。

demo code 1.0.2

問題2

這時我們增加了另一個頁面C。這個情境會稍微抽象一點,我們定義了3個資料

· 頁面A的資料dataA

· 頁面B的資料dataB

· 頁面C的資料dataC

問題1中 dataA = dataB。在問題2中dataA = dataB + dataC;

問題2 解決方案C

也就是說頁面C的修改,也會影響頁面A的資料,那麼我們是不是也要寫一個XXXXDelegate呢?

這時我們的大腦嗅出了一些不好的味道,如果再來個什麼dataD,dataE,我們要寫這麼多的Delegate嗎?對於多對一”通知”這種味道,很自然的想到了不用Delegate,而是用NSNotification來做。讓我們未雨綢繆一下,定義一個Notificaiton

12345 NSString *const kCUDataChangedNotification = @"CUDataChangedNotification"; [[NSNotificationCenter defaultCenter] postNotificationName:kCUDataChangedNotification                                                  object:nil                                                userInfo:nil];

那這個變化broadcast到listener,看上去是一個很贊的idea。

demo code 1.0.3

問題3

過了一段時間,我們發現問題2的方法有一個Bug,當介面停在頁面B的時候,切換到頁面C,修改資料,B中再返回時,資料和頁面A的資料不一致。

 

資料不一致

那也可以類比解決方案B,得到了下面的方法

解決方案D

既然A和B的資料不一致,而A的資料比B的新,那麼保留一個B的指標,然後A變化的時候,更新B就好了。

123456789101112131415 - (void)handleDataChangedNotification {    [self updateLabel];    [self.vc updateLabel];} // In a storyboard-based application, you will often want to do a little preparation before navigation- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{    if ([segue.identifier isEqualToString:@"push"]) {        CUDetailViewController *vc = [segue destinationViewController];        if ([vc isKindOfClass:[CUDetailViewController class]]) {            self.vc = vc;        }    }}

demo code 1.0.4

問題4

頁面C實在是太簡單了,這次我們希望在頁面C中顯示頁面A的資料。因為上次我們就產生了一個資料不一致的問題,這次我們注意到了,那麼怎麼修改呢?

解決方案E

在看了看整個APP各種通知之後,覺得挺麻煩,準備用一個取巧的方法。可以類比解決方案A。在頁面C出現的時候,重新整理資料,至於什麼效能問題,不管了,先fix bug。

12345678910 - (void)viewWillAppear:(BOOL)animated {    [self updateLabel];} - (void)updateLabel {    int dataB = [[CUDataDAO selectData].data intValue];    int dataC = [[CUDataDAO selectOtherData].data intValue];     self.dataLabel.text = [@(dataB + dataC) stringValue];}

demo code 1.0.5

問題5

這時的資料需要不斷的變化,我們在CUDataDAO加了一個timer 類比資料變化,資料變化的原因可能是server push 一些資料。client 本機資料庫更新了資料,需要在頁面A、B、C中顯示。

頁面C的資料又不一致了。。。。

問題到底在哪裡呢

走到這裡,我們需要重新思考為什麼這個問題會不斷的重複出現呢?software architecture就是來解決這個問題的。但是在提出一個合理的方案之前,先思考一個概念。

我們把資料庫中的資料,顯示到螢幕上,或是傳遞給View時,這個過程其實是對data 做了一次copy。而且只要不是通過引用或是指標這些方式,通過值傳遞的方式都是對

data做了一次copy。而這個copy的過程,非常類似Cache。

 

通常建立一個Cache會遇到2種問題。

· Cache情況A: 與original Data 資料不一致,沒有及時更新

· Cache情況B: 重複建立Cache

讓我們用這個思路來看我們的解決方案

解決方案A

這是一個非常典型的Cache情況B。資料庫的資料並沒有變化,但我們卻多次重複計算cache

解決方案B

頁面之間的關係可以用下面來描述

 

這裡我們隱隱能夠感覺到問題,A的資料變化依賴於2個地方。不急,再往後看

解決方案C

 

解決方案D:

 

 

 

事情變得更糟了

解決方案E

和解決方案A類似,同樣的重複計算Cache問題。

實際上問題還會更糟

現在還是一個簡單的Model,如果project變得很大,那麼就會變成這個樣子

每一個X都可能是一個Bug。

我們似乎已經找到問題了

《Advanced iOS Application Architecture and Patterns》 中,把這個圖叫做information flow。我們的直覺會告訴我們,這個資訊的傳遞,應該是自上而下的樹或是森林,而且最好是一個層次平衡結構,要清晰,每一個位置都有相對於的職責。那我們就需要制定一個規則。

在想這個規則之前,如果把上面的圖背後的資料忘記,我們感覺這很類似記憶體模型。當然記憶體模型會比較複雜。但是我們可以借鑒很多”記憶體管理中的規則”,比如誰建立,誰銷毀。同樣,在我們的information flow中,我們希望誰建立Cache,誰更新Cache變化

DAO的資料庫似乎很難做這件事情,我們引入了一個新的元素dataSource(當然他本身又是DAO的一個Cache)。其中A、B、C3個都會顯示資料,那麼他們應該在一個層級,其中B、C會修改資料,他們會把這個資料返回給dataSource,而通過dataSource來把這個變化通知到A、B、C。

這樣帶來的好處很明顯,我們再添加一個D,也不會對其他地方的資料產生任何影響,我們的Unit Test、Mock也更加好寫。

我們之前的思路錯在哪裡呢?

從局部來看,我們之前的思路都沒有任何問題,但是整體來看卻把問題隱藏化。關鍵的問題是在於沒有找到Truth,找到問題真正的地方。而找到真正的地方,需要我們在大腦中有一個清晰的information flow或是data flow。瞭解之間元素的相互關係,才能建立一個個的層。才能坐到真正的解耦,解耦並不是僅僅一個個的Manager,更重要的是建立一套清晰的flow機制,或是訊息機制,如果沒有一套flow,中間引入的各種各樣的方法,即便使用了各種設計模式,整個software 依然是深度耦合。

疑問

這個APP看上去互動非常複雜

上面的model,有些同學還可能覺得這是互動上面的問題,這個互動看上去非常的複雜,不是一個好設計。

我這裡列舉一個實際的例子:

A頁面要建立動畫,動畫背後包括很多資料,這些資料會在B,C甚至更多的頁面,或是後台被修改。動畫本身實際上體現在View,而這些view可能不僅僅在A中有,B,C可能也會有部分的View。

單例怎麼樣

當然我們可以用單例的法子。單例是個魔鬼,被很多濫用,這個情境用單例,其實僅僅是把全域變數合理的封裝在了單例下,因為這份資料,並沒有任何理由要一定是一份copy。

recap

在瞭解這個概念後,再看一些server的架構,規則時,也會更容易理解這些層之間的關係。包括

為什麼要規定那些層之間,不能相互調用,不能有靜態方法。

一個層之間的model,不能有重疊功能,不能連表查詢。

在哪個層才能調用另一個服務,而調用這個服務還必須要通過統一的介面

software architecture 涵蓋的東西非常多。這篇只是一個引子,介紹了設計之前的準備工作。但是在實際過程中,我們的模型可能要比我這裡寫的還要複雜很多。下一篇會介紹一種策略用來處理更加複雜模型的情況。

iOS項目架構

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.