<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                > 原文出處:http://www.infoq.com/cn/articles/ios-app-arch-2-3 iOS客戶端應用架構看似簡單,但實際上要考慮的事情不少。本文作者將以系列文章的形式來回答iOS應用架構中的種種問題,本文是其中的第二篇,主要講View層的組織和調用方案。下篇主要討論做View層架構的設計的一些心法。 ## 本門心法 > 重劍無鋒,大巧不工。 ---- 《神雕俠侶》 這是楊過在挑劍時,玄鐵重劍旁邊寫的一段話。對此我深表認同。提到這段話的目的是想告訴大家,在具體做View層架構的設計時,不需要拘泥于MVC、MVVM、VIPER等規矩。這些都是招式,告訴你你就知道了,然后怎么玩都可以。但是心法不是這樣的,心法是大巧,說出來很簡單,但是能不能在實際架構設計時牢記心法,并且按照規矩辦事,就都看個人了。 ## 拆分的心法 > 天下功夫出少林,天下架構出MVC。 ---- Casa Taloyum MVC其實是非常高Level的抽象,意思也就是,在MVC體系下還可以再衍生無數的架構方式,但萬變不離其宗的是,它一定符合MVC的規范。這句話不是我說的,是我在某個英文資料上看到的,但時過境遷,我已經找不到出處了,我很贊同這句話。我采用的架構嚴格來說也是MVC,但也做了很多的拆分。根據前面幾節的洗禮,相信各位也明白了這樣的道理:拆分方式的不同誕生了各種不同的衍生架構方案(MVCS拆胖Controller,MVVM拆胖Model,VIPER什么都拆),但即便拆分方式再怎么多樣,那都只是招式。而拆分的規范,就是心法。這一節我就講講我在做View架構時,做拆分的心法。 **第一心法:保留最重要的任務,拆分其它不重要的任務** 在iOS開發領域內,UIViewController承載了非常多的事情,比如View的初始化,業務邏輯,事件響應,數據加工等等,當然還有更多我現在也列舉不出來,但是我們知道有一件事情Controller肯定逃不掉要做:協調V和M。也就是說,不管怎么拆,協調工作是拆不掉的。 那么剩下的事情我們就可以拆了,比如UITableView的DataSource。唐巧的博客有一篇[文章](http://blog.devtang.com/blog/2015/03/15/ios-dev-controversy-1/)提到他和另一個工程師關于是否要拆分DataSource爭論了好久。拆分DataSource這個做法應該也算是通用做法,在不復雜的應用里面,它可能確實看上去只是一個數組而已,但在復雜的情況下,它背后可能涉及了文件內容讀取,數據同步等等復雜邏輯,[這篇文章](http://www.objc.io/issue-1/lighter-view-controllers.html)的第一節就提倡了這個做法,我其實也蠻提倡的。 前面的文章里面也提了很多能拆的東西,我就不搬運了,大家可以進去看看。除了這篇文章提到的內容以外,任何比較大的,放在ViewController里面比較臟的,只要不是Controller的核心邏輯,都可以考慮拆出去,然后在架構的時候作為一個獨立模塊去定義,以及設計實現。 **第二心法:拆分后的模塊要盡可能提高可復用性,盡量做到DRY** 根據第一心法拆開來的東西,很有可能還是強業務相關的,這種情況有的時候無法避免。但我們拆也要拆得好看,拆出來的部分最好能夠歸成某一類對象,然后最好能夠抽象出一個通用邏輯出來,使他能夠復用。即使不能抽出通用邏輯,那也盡量抽象出一個protocol,來實現IOP。這里有篇[關于IOP的文章](http://casatwy.com/tiao-chu-mian-xiang-dui-xiang-si-xiang-er-duo-tai.html),大家看了就明白優越性了。 **第三心法:要盡可能提高拆分模塊后的抽象度** 也就是說,拆分的粒度要盡可能大一點,封裝得要透明一些。唐巧說一切隱藏都是對代碼復雜性的增加,除非它帶來了好處,這在一定程度上有點道理,沒有好處的隱藏確實都不好(笑)。提高抽象度事實上就是增加封裝的力度,將一個負責的業務抽象成只需要很少的輸入就能完成,就是高度抽象。嗯,繼承很多層,這種做法雖然也提高了抽象程度,但我不建議這么玩。我不確定唐巧在這里說的隱藏跟我說的封裝是不是同一個概念,但我在這里想提倡的是盡可能提高抽象程度。 提高抽象程度的好處在于,對于業務方來說,他只需要收集很少的信息(最小充要條件),做很少的調度(Controller負責大模塊調度,大模塊里面再去做小模塊的調度),就能夠完成任務,這才是給Controller減負的正確姿勢。 如果拆分出來的模塊抽象程度不夠,模塊對外界要求的參數比較多,那么在Controller里面,關于收集參數的代碼就會多了很多。如果一部分參數的收集邏輯能夠由模塊來完成,那也可以做到幫Controller減輕負擔。否則就感覺拆得不太干凈,因為Controller里面還是多了一些不必要的參數收集邏輯。 如果拆分出來的粒度太小,Controller在完成任務的時候調度代碼要寫很多,那也不太好。導致拆分粒度小的首要因素就是業務可能本身就比較復雜,拆分粒度小并不是不好,能大就大一點,如果小了,那也沒問題。針對這種情況的處理,就需要采用strategy模式。 針對拆分粒度小的情況,我來舉個實際例子,這個例子來源于我的一個朋友他在做聊天應用的消息發送模塊。當消息是文字時,直接發送。當消息是圖片時,需要先向服務器申請上傳資源,獲得資源ID之后再上傳圖片,上傳圖片完成之后拿到圖片URL,后面帶著URL再把信息發送出去。 這時候我們拆模塊,可以拆成:數據發送(叫A模塊),上傳資源申請(叫B模塊),內容上傳(叫C模塊)。那么要發送文字消息,Controller調度A就可以了。如果要發送圖片消息,Controller調度B->C->A,假設將來還有上傳別的類型消息的任務,他們又要依賴D/E/F模塊,那這個事情就很蛋疼,因為邏輯復雜了,Controller要調度的東西要區分的情況就多了,Controller就膨脹了。 那么怎么處理呢?可以采用Strategy模式。我們再來分析一下,Controller要完成任務,它初始情況下所具有的條件是什么?它有這條消息的所有數據,也知道這個消息的類型。那么它最終需要的是什么呢?消息發送的結果:發送成功或失敗。 ![](https://box.kancloud.cn/2015-09-15_55f7de8ac1aaf.jpg) 上面就是我們要實現的最終結果,Controller只要把消息丟給MessageSender,然后讓MessageSender去做事情,做完了告訴Controller就好了。那么MessageSender里面怎么去調度邏輯?MessageSender里面可以有一個StrategyList,里面存放了表達各種邏輯的Block或者Invocation(Target-Action)。那么我們先定義一個Enum,里面規定了每種任務所需要的調度邏輯。 ~~~ typedef NS_ENUM (NSUInteger, MessageSendStrategy) { MessageSendStrategyText = 0, MessageSendStrategyImage = 1, MessageSendStrategyVoice = 2, MessageSendStrategyVideo = 3 } ~~~ 然后在MessageSender里面的StrategyList是這樣: ~~~ @property (nonatomic, strong) NSArray *strategyList; self.strategyList = @[TextSenderInvocation, ImageSenderInvocation, VoiceSenderInvocation, VideoSenderInvocation]; // 然后對外提供一個這樣的接口,同時有一個delegate用來回調 - (void)sendMessage:(BaseMessage *)message withStrategy:(MessageSendStrategy)strategy; @property (nonatomic, weak) id delegate; @protocol MessageSenderDelegate @required - (void)messageSender:(MessageSender *)messageSender didSuccessSendMessage:(BaseMessage *)message strategy:(MessageSendStrategy)strategy; - (void)messageSender:(MessageSender *)messageSender didFailSendMessage:(BaseMessage *)message strategy:(MessageSendStrategy)strategy error:(NSError *)error; @end ~~~ Controller里面是這樣使用的: ~~~ [self.messageSender sendMessage:message withStrategy:MessageSendStrategyText]; ~~~ MessageSender里面是這樣的: ~~~ [self.strategyList[strategy] invoke]; ~~~ 然后在某個Invocation里面,就是這樣的: ~~~ [A invoke]; [B invoke]; [C invoke]; ~~~ 這樣就好啦,即便拆分粒度因為客觀原因無法細化,那也能把復雜的判斷邏輯和調度邏輯從Controller中抽出來,真正為Controller做到了減負。總之能夠做到大粒度就盡量大粒度,實在做不到那也行,用Strategy把它hold住。這個例子是小粒度的情況,大粒度的情況太簡單,我就不舉了。 ## 設計心法 針對View層的架構不光是看重如何合理地拆分MVC來給UIViewController減負,另外一點也要照顧到業務方的使用成本。最好的情況是業務方什么都不知道,然后他把代碼放進去就能跑,同時還能獲得框架提供的種種功能。 比如天安門廣場上的觀眾看臺,就是我覺得最好的設計,因為沒人會注意到它。 **第一心法:盡可能減少繼承層級,涉及蘋果原生對象的盡量不要繼承** 繼承是罪惡,盡量不要繼承。就我目前了解到的情況看,除了安居客的Pad App沒有在框架級針對UIViewController有繼承的設計以外,其它公司或多或少都針對UIViewController有繼承,包括安居客iPhone app(那時候我已經對此無能為力,可見View的架構在一開始就設計好有多么重要)。甚至有的還對UITableView有繼承,這是一件多么令人發指,多么慘絕人寰,多么喪心病狂的事情啊。雖然不可避免的是有些情況我們不得不從蘋果原生對象中繼承,比如UITableViewCell。但我還是建議盡量不要通過繼承的方案來給原生對象添加功能,前面提到的Aspect方案和Category方案都可以使用。用Aspect+load來實現重載函數,用Category來實現添加函數,當然,耍點手段用Category來添加property也是沒問題的。這些方案已經覆蓋了繼承的全部功能,而且非常好維護,對于業務方也更加透明,何樂而不為呢。 不用繼承可能在思路上不會那么直觀,但是對于不使用繼承帶來的好處是足夠頂得上使用繼承的壞處的。順便在此我要給Category正一下名:業界對于Category的態度比較曖昧,在多種場合(講座、資料文檔)都宣揚過盡可能不要使用Category。它們說的都有一定道理,但我認為Category是蘋果提供的最好的使用集合代替繼承的方案,但針對Category的設計對架構師的要求也很高,請合理使用。而且蘋果也在很多場合使用Category,來把一個原本可能很大的對象,根據不同場景拆分成不同的Category,從而提高可維護性。 不使用繼承的好處我在[這里](http://casatwy.com/tiao-chu-mian-xiang-dui-xiang-si-xiang-yi-ji-cheng.html)已經說了,放到iOS應用架構來看,還能再多額外兩個好處:1\. 在業務方做業務開發或者做Demo時,可以脫離App環境,或花更少的時間搭建環境。2\. 對業務方來說功能更加透明,也符合業務方在開發時的第一直覺。 **第二心法:做好代碼規范,規定好代碼在文件中的布局,尤其是ViewController** 這主要是為了提高可維護性。在一個文件非常大的對象中,尤其要限制好不同類型的代碼在文件中的布局。比如在寫ViewController時,我之前給團隊制定的規范就是前面一段全部是getter setter,然后接下來一段是life cycle,viewDidLoad之類的方法都在這里。然后下面一段是各種要實現的Delegate,再下面一段就是event response,Button的或者GestureRecognizer的都在這里。然后后面是private method。一般情況下,如果做好拆分,ViewController的private method那一段是沒有方法的。后來隨著時間的推移,我發現開頭放getter和setter太影響閱讀了,所以后面改成全放在ViewController的最后。 **第三心法:能不放在Controller做的事情就盡量不要放在Controller里面去做** Controller會變得龐大的原因,一方面是因為Controller承載了業務邏輯,MVC的總結者(在正式提出MVC之前,或多或少都有人這么設計,所以說MVC的設計者不太準確)對Controller下的定義也是承載業務邏輯,所以Controller就是用來干這事兒的,天經地義。另一方面是因為在MVC中,關于Model和View的定義都非常明確,很少有人會把一個屬于M或V的東西放到其他地方。然后除了Model和View以外,還會剩下很多模棱兩可的東西,這些東西從概念上講都算Controller,而且由于M和V定義得那么明確,所以直覺上看,這些東西放在M或V是不合適的,于是就往Controller里面塞咯。 正是由于上述兩方面原因導致了Controller的膨脹。我們再細細思考一下,Model膨脹和View膨脹,要針對它們來做拆分其實都是相對容易的,Controller膨脹之后,拆分就顯得艱難無比。所以如果能夠在一開始就盡量把能不放在Controller做的事情放到別的地方去做,這樣在第一時間就可以讓你的那部分將來可能會被拆分的代碼遠離業務邏輯。所以我們要稍微轉變一下思路:模棱兩可的模塊,就不要塞到Controller去了,塞到V或者塞到M或者其他什么地方都比塞進Controller好,便于將來拆分。 所以關于前面我按下不表的關于胖Model和瘦Model的選擇,我的態度是更傾向于胖Model。客觀地說,業務膨脹之后,代碼規模肯定少不了的,不管你技術再好,經驗再豐富,代碼量最多只能優化,該膨脹還是要膨脹的,而且優化之后代碼往往也比較難看,使用各種奇技淫巧也是有代價的。所以,針對代碼量優化的結果,往往要么就是犧牲可讀性,要么就是犧牲可移植性(通用性),Every magic always needs a pay, you have to make a trade-off.。 那么既然膨脹出來的代碼,或者將來有可能膨脹的代碼,不管放在MVC中的哪一個部分,最后都是要拆分的,既然遲早要拆分,那不如放Model里面,這樣將來拆分胖Model也能比拆分胖Cotroller更加容易。在我還在安居客的時候,安居客Pad app承載最復雜業務的ViewController才不到600行,其他多數Controller都是在300-400行之間,這就為后面接手的人降低了非常多的上手難度和維護復雜度。拆分出來的東西都是可以直接遷移給iPhone app使用的。現在看天貓的ViewControler,動不動就幾千行,看不了多久頭就暈了,問了一下,大家都表示很習慣這樣的代碼長度,攤手。 **第四心法:架構師是為業務工程師服務的,而不是去使喚業務工程師的** 架構師在公司里的職級和地位往往都是要高于業務工程師的,架構師的技術實力和經驗往往也都是高于業務工程師的。所以你值得在公司里獲得較高的地位,但是在公司里的地位高不代表在軟件工程里面的角色地位也高。架構師是要為業務工程師服務的,是他們使喚你而不是你使喚他們。另外,制定規范一方面是起到約束業務工程師的代碼,但更重要的一點是,這其實是利用你的能力幫助業務工程師避免他無法預見的危機,所以地位高有一定的好處,畢竟夏蟲不可語冰,有的時候不見得能夠解釋得通,因此高地位隨之而來的就是說服力會比較強。但在軟件工程里,一定要保持謙卑,一定要多為業務工程師考慮。 一個不懂這個道理的架構師,設計出來的東西往往復雜難用,因為他只愿意做核心的東西,周邊不愿意做的都期望交給業務工程師去做,甚至有的時候就只做了個Demo,然后就交給業務工程師了,業務工程師變成給他打工的了。但是一個懂得這個道理的架構師,設計出來的東西會非常好用,業務方只需要扔很少的參數然后拿結果就好了,這樣的架構才叫好的架構。 舉一個保存圖片到本地的例子,一種做法是提供這樣的接口:- (NSString *)saveImageWithData:(NSData *)imageData,另一種是- (NSString *)saveImage:(UIImage *)image。后者更好,原因自己想。 你的態度越謙卑,就越能設計出好的架構,這是我設計心法里的最后一條,也是最重要的一條。即使你現在技術實力不是業界大牛級別的,但只要保持這個心態去做架構,去做設計,就已經是合格的架構師了,要成為業界大牛也會非常快。 ## 小總結 其實針對View層的架構設計,還是要做好三點:代碼規范,架構模式,工具集。 代碼規范對于View層來說意義重大,畢竟View層非常重業務,如果代碼布局混亂,后來者很難接手,也很難維護。 架構模式具體如何選擇,完全取決于業務復雜度。如果業務相當相當復雜,那就可以使用VIPER,如果相對簡單,那就直接MVC稍微改改就好了。每一種已經成為定式的架構模式不見得都適合各自公司對應的業務,所以需要各位架構師根據情況去做一些拆分或者改變。拆分一般都不會出現問題,改變的時候,只要別把MVC三個角色搞混就好了,M該做啥做啥,C該做啥做啥,V該做啥做啥,不要亂來。關于大部分的架構模式應該是什么樣子,這篇文章里都已經說過了,不過我認為最重要的還是后面的心法,模式只是招術,熟悉了心法才能大巧不工。 View層的工具集主要還是集中在如何對View進行布局,以及一些特定的View,比如帶搜索提示的搜索框這種。這篇文章只提到了View布局的工具集,其它的工具集相對而言是更加取決于各自公司的業務的,各自實現或者使用CocoaPods里現成的都不是很難。 對于小規模或者中等規模iOS開發團隊來說,做好以上三點就足夠了。在大規模團隊中,有一個額外問題要考慮,就是跨業務頁面調用方案的設計。 ## 跨業務頁面調用方案的設計 跨業務頁面調用是指,當一個App中存在A業務,B業務等多個業務時,B業務有可能會需要展示A業務的某個頁面,A業務也有可能會調用其他業務的某個頁面。在小規模的App中,我們直接import其他業務的某個ViewController然后或者push或者present,是不會產生特別大的問題的。但是如果App的規模非常大,涉及業務數量非常多,再這么直接import就會出現問題。 ![](https://box.kancloud.cn/2015-09-15_55f7de8b63cec.jpg) 可以看出,跨業務的頁面調用在多業務組成的App中會導致橫向依賴。那么像這樣的橫向依賴,如果不去設法解決,會導致什么樣的結果? 1. 當一個需求需要多業務合作開發時,如果直接依賴,會導致某些依賴層上端的業務工程師在前期空轉,依賴層下端的工程師任務繁重,而整個需求完成的速度會變慢,影響的是團隊開發迭代速度。 2. 當要開辟一個新業務時,如果已有各業務間直接依賴,新業務又依賴某個舊業務,就導致新業務的開發環境搭建困難,因為必須要把所有相關業務都塞入開發環境,新業務才能進行開發。影響的是新業務的響應速度。 3. 當某一個被其他業務依賴的頁面有所修改時,比如改名,涉及到的修改面就會特別大。影響的是造成任務量和維護成本都上升的結果。 當然,如果App規模特別小,這三點帶來的影響也會特別小,但是在阿里這樣大規模的團隊中,像天貓/淘寶這樣大規模的App,一旦遇上這里面哪怕其中一件事情,就很坑爹。 ## 那么應該怎樣處理這個問題? 讓依賴關系下沉。 怎么讓依賴關系下沉?引入Mediator模式。 所謂引入Mediator模式來讓依賴關系下沉,實質上就是每次呼喚頁面的時候,通過一個中間人來召喚另外一個頁面,這樣只要每個業務依賴這個中間人就可以了,中間人的角色就可以放在業務層的下面一層,這就是依賴關系下沉。 ![](https://box.kancloud.cn/2015-09-15_55f7de8be1473.jpg) 當A業務需要調用B業務的某個頁面的時候,將請求交給Mediater,然后由Mediater通過某種手段獲取到B業務頁面的實例,交還給A就行了。在具體實現這個機制的過程中,有以下幾個問題需要解決: 1. 設計一套通用的請求機制,請求機制需要跟業務剝離,使得不同業務的頁面請求都能夠被Mediater處理 2. 設計Mediater根據請求如何獲取其他業務的機制,Mediater需要知道如何處理請求,上哪兒去找到需要的頁面 這個看起來就非常像我們web開發時候的URL機制,發送一個Get或Post請求,CGI調用腳本把請求分發給某個Controller下的某個Action,然后返回HTML字符串到瀏覽器去解析。蘋果本身也實現了一套跨App調用機制,它也是基于URL機制來運轉的,只不過它想要解決的問題是跨App的數據交流和頁面調用,我們想要解決的問題是降低各業務的耦合度。 不過我們還不能直接使用蘋果原生的這套機制,因為這套機制不能夠返回對象實例。而我們希望能夠拿到對象實例,這樣不光可以做跨業務頁面調用,也可以做跨業務的功能調用。另外,我們又希望我們的Mediater也能夠跟蘋果原生的跨App調用兼容,這樣就又能幫業務方省掉一部分開發量。 就我目前所知道的情況,AutoCad旗下某款iOS應用(時間有點久我不記得是哪款應用了,如果你是AutoCad的iOS開發,可以在評論區補充一下。)就采用了這種頁面調用方式。天貓里面目前也在使用這套機制,只是這一塊由于歷史原因存在新老版本混用的情況,因此暫時還沒能夠很好地發揮應有的作用。 ## 關于Getter和Setter? 我比較習慣一個對象的"私有"屬性寫在extension里面,然后這些屬性的初始化全部放在getter里面做,在init和dealloc之外,是不會出現任何類似_property這樣的寫法的。就是這樣: ~~~ @interface CustomObject() @property (nonatomic, strong) UILabel *label; @end @implement #pragma mark - life cycle - (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.label]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.label.frame = CGRectMake(1, 2, 3, 4); } #pragma mark - getters and setters - (UILabel *)label { if (_label == nil) { _label = [[UILabel alloc] init]; _label.text = @"1234"; _label.font = [UIFont systemFontOfSize:12]; ... ... } return label; } @end ~~~ 唐巧說他喜歡的做法是用_property這種,然后關于_property的初始化通過[self setupProperty]這種做法去做。從剛才上面的代碼來看,就是要在viewDidLoad里面多調用一個setup方法而已,然后我推薦的方法就是不用多調一個setup方法,直接走getter。 嗯,怎么說呢,其實兩種做法都能完成需求。但是從另一個角度看,蘋果之所以選擇讓[self getProperty]和self.property可以互相通用,這種做法已經很明顯地表達了蘋果的傾向:希望每個property都是通過getter方法來獲得。 早在2003年,Allen Holub就發了篇文章《[Why getter and setter methods are evil](http://www.javaworld.com/article/2073723/core-java/why-getter-and-setter-methods-are-evil.html)》,自此之后,業界就對此產生了各種爭議,雖然是從Java開始說的,但是發展到后面各種語言也參與了進來。然后雖然現在關于這個問題討論得少了,但是依舊屬于沒有定論的狀態。setter的情況比較復雜,也不是我這一節的重點,我這邊還是主要說getter。我們從objc的設計來看,蘋果的設計者更加傾向于getter is not evil。 認為getter is evil的原因有非常之多,或大或小,隨著爭論的進行,大家慢慢就聚焦到這樣的一個原因:Getter和Setter提供了一個能讓外部修改對象內部數據的方式,這是evil的,正常情況下,一個對象自己私有的變量應該是只有自己關心。 然后我們回到iOS領域來,objc也同樣面臨了這樣的問題,甚至更加嚴重:objc并沒有像Java那么嚴格的私有概念。但在實際工作中,我們不太會去操作頭文件里面沒有的變量,這是從規范上就被禁止的。 認為getter is not evil的原因也可以聚焦到一個:高度的封裝性。getter事實上是工廠方法,有了getter之后,業務邏輯可以更加專注于調用,而不必擔心當前變量是否可用。我們可以想一下,假設一個ViewController有20個subview要加入view中,這20個subview的初始化代碼是肯定逃不掉的,放在哪里比較好?放在哪里都比放在addsubview的地方好,我個人認為最好的地方還是放在getter里面,結合單例模式之后,代碼會非常整齊,生產的地方和使用的地方得到了很好的區分。 所以放到iOS來說,我還是覺得使用getter會比較好,因為evil的地方在iOS這邊基本都避免了,not evil的地方都能享受到,還是不錯的。 ## 總結 要做一個View層架構,主要就是從以下三方面入手: 1. 制定良好的規范 2. 選擇好合適的模式(MVC、MVCS、MVVM、VIPER) 3. 根據業務情況針對ViewController做好拆分,提供一些小工具方便開發 當然,你還會遇到其他的很多問題,這時候你可以參考這篇文章里提出的心法,在后面提到的跨業務頁面調用方案的設計中,你也能夠看到我的一些心法的影子。 對于iOS客戶端來說,它并不像其他語言諸如Python、PHP他們有那么多的非官方通用框架。客觀原因在于,蘋果已經為我們做了非常多的事情,做了很多的努力。在蘋果已經做了這么多事情的基礎上,架構師要做針對View層的方案時,最好還是盡量遵守蘋果已有的規范和設計思想,然后根據自己過去開發iOS時的經驗,盡可能給業務方在開發業務時減負,提高業務代碼的可維護性,就是View層架構方案的最大目標。 ## 關于AOP AOP(Aspect Oriented Programming),面向切片編程,這也是面向XX編程系列術語之一哈,但它跟我們熟知的面向對象編程沒什么關系。 **什么是切片?** 程序要完成一件事情,一定會有一些步驟,1,2,3,4這樣。這里分解出來的每一個步驟我們可以認為是一個切片。 **什么是面向切片編程?** 你針對每一個切片的間隙,塞一些代碼進去,在程序正常進行1,2,3,4步的間隙可以跑到你塞進去的代碼,那么你寫這些代碼就是面向切片編程。 **為什么會出現面向切片編程?** 你要想做到在每一個步驟中間做你自己的事情,不用AOP也一樣可以達到目的,直接往步驟之間塞代碼就好了。但是事實情況往往很復雜,直接把代碼塞進去,主要問題就在于:塞進去的代碼很有可能是跟原業務無關的代碼,在同一份代碼文件里面摻雜多種業務,這會帶來業務間耦合。為了降低這種耦合度,我們引入了AOP。 **如何實現AOP?** AOP一般都是需要有一個攔截器,然后在每一個切片運行之前和運行之后(或者任何你希望的地方),通過調用攔截器的方法來把這個jointpoint扔到外面,在外面獲得這個jointpoint的時候,執行相應的代碼。 在iOS開發領域,objective-C的runtime有提供了一系列的方法,能夠讓我們攔截到某個方法的調用,來實現攔截器的功能,這種手段我們稱為Method Swizzling。Aspects通過這個手段實現了針對某個類和某個實例中方法的攔截。 另外,也可以使用protocol的方式來實現攔截器的功能,具體實現方案就是這樣: ~~~ @protocol RTAPIManagerInterceptor @optional - (void)manager:(RTAPIBaseManager *)manager beforePerformSuccessWithResponse:(AIFURLResponse *)response; - (void)manager:(RTAPIBaseManager *)manager afterPerformSuccessWithResponse:(AIFURLResponse *)response; - (void)manager:(RTAPIBaseManager *)manager beforePerformFailWithResponse:(AIFURLResponse *)response; - (void)manager:(RTAPIBaseManager *)manager afterPerformFailWithResponse:(AIFURLResponse *)response; - (BOOL)manager:(RTAPIBaseManager *)manager shouldCallAPIWithParams:(NSDictionary *)params; - (void)manager:(RTAPIBaseManager *)manager afterCallingAPIWithParams:(NSDictionary *)params; @end @interface RTAPIBaseManager : NSObject @property (nonatomic, weak) id interceptor; @end ~~~ 這么做對比Method Swizzling有個額外好處就是,你可以通過攔截器來給攔截器的實現者提供更多的信息,便于外部實現更加了解當前切片的情況。另外,你還可以更精細地對切片進行劃分。Method Swizzling的切片粒度是函數粒度的,自己實現的攔截器的切片粒度可以比函數更小,更加精細。 缺點就是,你得自己在每一個插入點把調用攔截器方法的代碼寫上(笑),通過Aspects(本質上就是Mehtod Swizzling)來實現的AOP,就能輕松一些。 ## 編后語 為了更好地向讀者輸出更優質的內容,InfoQ將精選來自國內外的優秀文章,經過整理審校后,發布到網站。本篇文章作者為**田偉宇**,原文鏈接為[Casa Taloyum](http://casatwy.com/iosying-yong-jia-gou-tan-viewceng-de-zu-zhi-he-diao-yong-fang-an.html)。本文已由原作者授權InfoQ中文站轉載。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看