重寫iOS項(xiàng)目 淺談iOS架構(gòu)

背景

在公司寫iOS項(xiàng)目异希,但是好幾個(gè)月沒寫代碼了(沒寫什么有意義的代碼了),大概一兩年前公司的一位前輩開發(fā)了一套便于快速開發(fā)的框架绒瘦,我們每天就是照貓畫虎称簿,寫著重復(fù)的代碼,說實(shí)話這種工作...額...找個(gè)樓管大媽培訓(xùn)一個(gè)月惰帽,開發(fā)起來也沒問題憨降;在這樣下去自己要被遺忘于江湖了,所以下了個(gè)決心重寫項(xiàng)目...搞完后该酗,通過老項(xiàng)目授药,蘋果api,新項(xiàng)目對比呜魄,淺談下iOS開發(fā)的一些心得體會(huì)悔叽。

前言

面對網(wǎng)上各種框架,各種編程方式爵嗅,各種詞匯的出現(xiàn)娇澎,好多人會(huì)感到不知所措,感覺要學(xué)的東西太多睹晒,學(xué)不過來了趟庄;面向過程括细,面向?qū)ο螅嫦蚯忻嫫萆叮嫦蚝瘮?shù)奋单,面向協(xié)議; MVC MVVM.... pop oop vop 各種p,but...親猫十,莫慌(就不要抱我了)览濒,請不要拘泥于形式,我們不要做跟隨者炫彩,要知道這些概念的出現(xiàn)都是為了解決實(shí)際問題匾七,這些名詞只是在為代碼的內(nèi)聚絮短,耦合江兢,復(fù)用,靈活...找到一個(gè)最佳的平衡點(diǎn)丁频,是在解決問題的過程中總結(jié)而形成的杉允;黑貓、白貓席里,能抓老鼠的就是好貓叔磷。請記住精通的目的在于應(yīng)用。你的某些代碼是解決某個(gè)實(shí)際問題的最佳實(shí)踐奖磁,那就是最好的架構(gòu)改基,最漂亮的代碼;

我很多時(shí)候不太會(huì)按常理出牌咖为,文章也是抱著初生牛犢不怕虎的心態(tài)寫的秕狰,在我這里規(guī)則是用來打破的,一些代碼的封裝思想可能不是很符合你的編程習(xí)慣躁染,但是能激起你的一些思考鸣哀;特別是子view的點(diǎn)擊事件與控制器之間的通信處理引發(fā)的一系列問題這一部分內(nèi)容,可以仔細(xì)閱讀吞彤,比較精彩...

客戶端開發(fā)框架漫談

說說公司老項(xiàng)目的框架體系:老項(xiàng)目是基于UITableview 和 cell 進(jìn)行深度定制的我衬,在界面上的每個(gè)UI模塊都是UITableviewCell,你沒有看錯(cuò)饰恕,每個(gè)UI模塊(一個(gè)整體)都是Cell挠羔,開發(fā)中,只需要用xib描述一個(gè)cell埋嵌,用一個(gè)字典指定好cell中每個(gè)UI組件對應(yīng)的模型中的字段破加,然后會(huì)自動(dòng)映射數(shù)據(jù);搞定莉恼,收工拌喉;圖解框架大概如下:

Snip20150803_1.png

框架的作者充分利用了tableView速那,開發(fā)快速方便,控制器的代碼相對較少尿背,作者想通過一個(gè)大牛逼viewcontroller搞定一切需求端仰,這樣做確實(shí)方便,開發(fā)者們不用動(dòng)什么腦筋就能寫完業(yè)務(wù)田藐;框架在設(shè)計(jì)的過程中難點(diǎn)在于cell的數(shù)據(jù)映射荔烧,可變cell的高度;還有就是這個(gè)大牛逼viewcontroller的封裝汽久;
優(yōu)點(diǎn) : 開發(fā)快速 邏輯實(shí)現(xiàn)部分代碼量少(cell通過xib描述) 代碼邏輯比較清晰鹤竭,易于閱讀;
缺點(diǎn) : 靈活性很差景醇,viewcontroller封裝的功能過多臀稚;不能做出好的用戶體驗(yàn)把沼,模式單一形帮;嚴(yán)重依賴數(shù)據(jù)模型驅(qū)動(dòng),如果網(wǎng)絡(luò)請求失敗橄仍,沒有數(shù)據(jù)散劫,界面將一片空白稚机,連一些靜態(tài)數(shù)據(jù)也顯示不出來;將所有的功能通過一個(gè)類全部包裝获搏,很多時(shí)候會(huì)造成小題大做赖条,代碼冗余量大;
結(jié)論 : 非常的不靈活常熙,沒有了靈活性 對客戶端來說幾乎是致命纬乍;如果對比一下蘋果api的繼承體系,不難發(fā)現(xiàn)上邊這套框架的思想和蘋果api設(shè)計(jì)思想是背離的症概。

簡單的看一下蘋果api的設(shè)計(jì)(分層封裝共性功能蕾额、分支細(xì)化小功能)

UIView的繼承體系:并非全部view,只是為了說明問題彼城,畫圖不是很專業(yè)诅蝶,海涵
在iOS當(dāng)中,所有的視圖都從一個(gè)叫做UIVIew的基類派生而來

Snip20150803_3.png

看上去像是一個(gè)層級關(guān)系樹管理了一些矩形塊募壕。

viewController的繼承體系

Snip20150803_4.png

tips: 如果你是iOS初學(xué)者调炬,我推薦在學(xué)習(xí)過程中通過查看頭文件整理出如圖的繼承體系,然后開始系統(tǒng)的學(xué)習(xí)(例如你要學(xué)習(xí)CALayer舱馅,你可以整理出CALayer和它子類的繼承體系: CATransformLayer, CATextLayer, CAShapeLayer..., 然后逐個(gè)突破學(xué)習(xí))缰泡,這樣當(dāng)你學(xué)會(huì)了UIControl中的一個(gè)方法:- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents; 就知道了所有繼承自UIControl的view都具有該方法(舉個(gè)例子); 蘋果api中類似這樣的圖可以畫出好多張,CAAnimation體系棘钞,controller體系缠借,CALayer體系...

回到正題,在編程界宜猜,你會(huì)經(jīng)称梅担看到*** 分層 這件事,其實(shí)整個(gè)計(jì)算機(jī)學(xué)科里甚至是整個(gè)人類科學(xué)中姨拥,分層設(shè)計(jì)都是一個(gè)很重要的概念绅喉,我最早把分層當(dāng)回事是在大學(xué)時(shí)期的計(jì)算機(jī)網(wǎng)絡(luò)課程中,在網(wǎng)絡(luò)領(lǐng)域中眾所周知的OSI參考模型體系標(biāo)準(zhǔn)叫乌,將網(wǎng)絡(luò)分成了7層架構(gòu)的參考模型柴罐,后來又被TCP/IP參考模型分為了我們所熟知的4層網(wǎng)絡(luò)體系架構(gòu);
分層設(shè)計(jì)一個(gè)很明顯的好處是:分層可以梯度的降低問題的復(fù)雜度憨奸,簡化問題革屠,也是解決某些難題的突破口;分層后的每一層功能相對單一膀藐,實(shí)現(xiàn)起來容易了許多屠阻;
另一個(gè)顯而易見的好處是:容易拓展功能和后期的維護(hù),由于分層后每層的職責(zé)單一额各,相互直接依賴降低了,裝逼的說法就是耦合度低了吧恃,但是不能說相互完全不受影響虾啦,不依賴,畢竟“耦合”的
藕斷絲連*痕寓;
其他好處...


通過上邊兩張圖傲醉,大體掌握了蘋果api中的View家族的設(shè)計(jì):UIView直接派生出3個(gè)大方向的View:

  • 能滾動(dòng)的view : UIScrollView及其子類
  • 需要交互的view: UIControl及其子類
  • 只是簡單的展示數(shù)據(jù),不需要交互也不需要滾動(dòng)呻率,例如UILabel....

這樣梳理 我們就很清楚蘋果UIView的劃分體系了硬毕,掌握起來就沒那么困難了;這其中的分層方式礼仗,思想吐咳,值得我們?nèi)W(xué)習(xí)、體會(huì)元践、實(shí)踐韭脊,畢竟我們每個(gè)人都是站在巨人的肩膀上編程的。
另一種分層方式 MVC MVVM...派系

  • 這種分層不是對某個(gè)家族進(jìn)行分層单旁,不像UIView或者Core Animation屬于家族分層(說白了就是: "誰應(yīng)該是誰的兒子"的問題)沪羔,
  • MVC MVVM 是對整個(gè)項(xiàng)目代碼組織結(jié)構(gòu)的劃分,它的規(guī)則是為了指定你的代碼應(yīng)該寫在哪個(gè)文件中象浑,你應(yīng)該如何處理項(xiàng)目中代碼的整體結(jié)構(gòu)蔫饰,這更像是三國時(shí)期的魏蜀吳琅豆,地盤到底該怎么分?
  • 使用mvc開發(fā)iOS程序的一個(gè)問題是,model層太單甭ㄓ酢(這貨幾乎什么都沒干)趋距,controller中堆了大量的邏輯,導(dǎo)致代碼成千上萬行...越除,于是就考慮能不能model層直接承擔(dān)UI特定的接口和屬性(個(gè)人愚見)节腐,于是就整出了:Model-View-ViewModel 的概念。我沒用過摘盆,所以理解有限翼雀,但是可以肯定的是MVVM是代碼如何組織和結(jié)構(gòu)化的另一種形式,至于適不適合iOS開發(fā)孩擂,怎么在iOS開發(fā)中使用狼渊,又是另一個(gè)大的話題了。

想明白這些東西后类垦,如你所見狈邑,框架沒什么神秘高深的,就是如何組織代碼蚤认,整體和局部如何分層米苹,然后就開始自己的代碼封裝之路;

搭建項(xiàng)目,先搞定網(wǎng)絡(luò)層: AFN + MJExtension的最佳實(shí)踐

要不要對AFN進(jìn)行封裝砰琢?
AFN本來用起來已經(jīng)很方便了蘸嘶,如果你的項(xiàng)目規(guī)模很小,接口十幾到二十個(gè)陪汽,頁面也沒多少训唱,這樣的話,沒必要了挚冤。如果項(xiàng)目規(guī)模較大况增,有幾十個(gè)控制器,每個(gè)控制器中都有個(gè)AFHTTPRequestOperationManager训挡,如果AFN升級或者想要換其他的網(wǎng)絡(luò)庫澳骤,或者做一些統(tǒng)一處理,那么工作量就來了舍哄,這種情況就很有必要對AFN進(jìn)行二次封裝宴凉。

封裝思路

  • 封裝AFN,即屏蔽AFN在你的代碼的侵入性表悬,所以要將AFN統(tǒng)一處理到一個(gè)類中弥锄,也就是一個(gè)類 has a AFN;
  • 切割url,針對不同的host進(jìn)行切割url籽暇,實(shí)際開發(fā)中温治,接口可能來自不同的host,即使是單一host請求戒悠,也得經(jīng)常在demo 和 dev熬荆、online之間切換host,最終可以將host主機(jī)地址處理到plist文件中绸狐,方便切換卤恳,由此,我們額外需要一個(gè)解析plist文件的工具類寒矿;
  • AFN請求回來的數(shù)據(jù)默認(rèn)都是字典突琳,我們不想面向字典開發(fā),希望網(wǎng)絡(luò)層在請求完數(shù)據(jù)后直接轉(zhuǎn)成模型符相,引入MJExtension(字典轉(zhuǎn)模型框架)拆融,用模型是面向?qū)ο缶幊痰囊环N思想,用模型可以用點(diǎn)語法啊终,避免了字典中key的字符串拼寫錯(cuò)誤镜豹,必要時(shí)請求參數(shù)也可以用模型,開發(fā)中有時(shí)候會(huì)因?yàn)樵谧值渲胁迦肽硞€(gè)空值蓝牲,導(dǎo)致應(yīng)用程序崩潰趟脂,如果我們先用模型寫好請求參數(shù),然后通過MJExtension在將模型轉(zhuǎn)為字典搞旭,就能有效避免這個(gè)問題散怖,對于模型中為nil的字段,MJExtension會(huì)自動(dòng)過濾肄渗,前提是字段是非基本數(shù)據(jù)類型;
  • AFN請求回調(diào)的block有個(gè)成功的block和失敗的block,兩個(gè)block代碼有些繁瑣咬最,事實(shí)上我們僅關(guān)心成功后的有效數(shù)據(jù)和失敗后的錯(cuò)誤信息翎嫡,所以將兩個(gè)block合并在一個(gè)block中,通過兩個(gè)參數(shù)分別接收有效數(shù)據(jù)和錯(cuò)誤信息永乌,并且這個(gè)參數(shù)的值是否存在必定是互斥的惑申,即如果有效數(shù)據(jù)有值,錯(cuò)誤信息必定是nil翅雏,反之亦然圈驼;

這部分內(nèi)容較多,這里簡單演示下封裝后使用的精簡程度望几,具體說明和demo可以前往github 绩脆,有詳細(xì)說明,歡迎交流,共同進(jìn)步靴迫。
業(yè)務(wù)類接口的實(shí)現(xiàn)

// .h文件
+(void)getDemoDataWithResponseHandler:(responseHandler)Handler;

// .m實(shí)現(xiàn)文件  
+(void)getDemoDataWithResponseHandler:(responseHandler)Handler
{
    [self getWithUrl:demoDataUrl param:nil resultClass:[DemoAllData class] responseBlock:Handler];
}

控制器中的使用

-(void)loadNetData
{
    [AppDemoServices getDemoDataWithResponseHandler:^(DemoAllData *dataObj, NSError *error) {
        if (dataObj) {
            [self.datas removeAllObjects];
            [self.datas addObjectsFromArray:dataObj.data];
            [self.tableView reloadData];
        } else {
            NSLog(@"網(wǎng)絡(luò)請求發(fā)生錯(cuò)誤");
        }
    }];
}  

說明:筆者工作經(jīng)驗(yàn)并不是很豐富惕味,文章也是學(xué)習(xí)成長的一些總結(jié)和感受,如果覺得覺得水準(zhǔn)太差玉锌,還請多多指教;


接下來:子view的點(diǎn)擊事件與控制器之間的通信處理引發(fā)的一系列問題

Snip20150806_11.png

圖中的灰色背景的View內(nèi)部有兩個(gè)按鈕名挥;雖然簡單但是能說明一些問題;

對于已經(jīng)添加在控制器view中的視圖主守,如果還要對其引用(使用property)禀倔,最好用weak 弱指針引用;
@property (weak, nonatomic) UIView *lightGrayView;
因?yàn)橐晥D已經(jīng)加在了控制器的view中参淫,控制器的view已經(jīng)對其強(qiáng)引用救湖,控制器又被導(dǎo)航控制器...最后application在管理著他們,所以你在引用時(shí)沒必要用strong引用黄刚;

小插曲播完捎谨,再回到正題。圖中的灰色view憔维,在開發(fā)中很常見涛救,即使比這個(gè)復(fù)雜許多的view,分析切割后业扒,縮影就是這樣检吆;對于創(chuàng)建這個(gè)灰色view的,我個(gè)人習(xí)慣是單獨(dú)封裝到一個(gè)view類中程储,除非這個(gè)view特別簡單蹭沛;單獨(dú)將封裝灰色view,又涉及到前邊說的代碼分層問題章鲤。

  • 基于MVC摊灭,在控制器中只是對這個(gè)view的填充數(shù)據(jù),有時(shí)候可能會(huì)處理交互败徊;
  • 代碼層次清晰帚呼,封裝后可以提高復(fù)用率,便于維護(hù);
  • 能大規(guī)模減少控制器的代碼行數(shù)皱蹦;

封裝后面臨另外一個(gè)問題煤杀,就是事件交互,由于控制器并不涉及灰色view內(nèi)部按鈕的創(chuàng)建代碼沪哺,所以不能直接監(jiān)聽到灰色view內(nèi)部按鈕的點(diǎn)擊事件沈自,需要傳遞事件;
iOS中不同對象間事件傳遞方式有3種:block 代理 通知; 其實(shí)這里稱為代理個(gè)人感覺并不是非常合適辜妓,代理是一種設(shè)計(jì)模式枯途,很多語言開發(fā)中都會(huì)用到忌怎,比較廣義;iOS中的對象間交互多為"數(shù)據(jù)傳遞" 和 "事件傳遞"柔袁,或者叫"數(shù)據(jù)源"和"委托"呆躲,通過tableview的datasource和delegate可以體會(huì)到,為了方便交流很多人都稱為代理捶索,而實(shí)現(xiàn)這兩種模式的基石就是協(xié)議插掂; 貌似整個(gè)cocoa框架都是基于協(xié)議建立起來的;所以我們自己寫的時(shí)候也盡量多用協(xié)議來完成通信腥例,能很好的和cocoa代碼想融合辅甥;
如果寫協(xié)議方法和使用委托就不多說了,需要注意的是燎竖,委托對象(delegate)要用weak來解除保留環(huán)璃弄;

// 聲明委托對象屬性
@property (weak, nonatomic) id<YKViewClickProtcol> clickDelegate;  

對于調(diào)用委托方法,通常都是這么寫的:

if ([self.clickDelegate respondsToSelector:@selector(goActionWithName:withObject:withSender:)]) {
        [self.clickDelegate goActionWithName:strName withObject:obj withSender:sender];
    };

這里如果委托對象為nil构回,給空對象發(fā)送消息夏块,if條件為false,所以不會(huì)執(zhí)行條件體纤掸,并不會(huì)導(dǎo)致程序崩潰脐供,所以不需要在條件中先判斷委托對象是否存在;
問題在于借跪,如果定義的協(xié)議中方法較多政己,且多為可選實(shí)現(xiàn),那么會(huì)寫出一大堆這樣的代碼掏愁,而頻繁的執(zhí)行if判斷除了第一次有用歇由,后邊的if檢查基本上是多余的,因?yàn)橐粋€(gè)委托對象一旦指定了幾乎不會(huì)改變果港;所以這里可以緩存檢查結(jié)果沦泌,來提升一些執(zhí)行效率;

tips--緩存: 可以在delegate的set方法中實(shí)現(xiàn)緩存(緩存委托對象是否響應(yīng)某個(gè)方法的結(jié)果)辛掠,如果委托對象不發(fā)生改變赦肃,set方法只會(huì)執(zhí)行一次,如果改了代理對象公浪,也肯定會(huì)重新調(diào)用set進(jìn)行賦值,所以在delegate的setter中檢查協(xié)議方法和并且緩存檢查結(jié)果自然是不錯(cuò)的方案船侧;
關(guān)于緩存最好方式是通過二進(jìn)制位欠气,程序執(zhí)行效率和資源消耗本來就是一個(gè)權(quán)衡問題,想提升程序執(zhí)行效率镜撩,必然會(huì)對內(nèi)存產(chǎn)生一定的消耗预柒,所以很多情況下我們要權(quán)衡利弊队塘;這里委托對象是否響應(yīng)某個(gè)方法的結(jié)果只有兩種情況 "響應(yīng)" 和 “未響應(yīng)”;一個(gè)二進(jìn)制位剛好宜鸯;定義這個(gè)二進(jìn)制位憔古,可以使用c語言中的 "位域";
struct {
unsigned int delegateMethod1: 1;
...
}
協(xié)議中的每個(gè)方法對應(yīng)一個(gè)二進(jìn)制位淋袖,進(jìn)行緩存鸿市;

這里的緩存并沒有對執(zhí)行效率有明顯的提升,現(xiàn)在手機(jī)的硬件能力都有很大的冗余即碗,如果過分在乎性能這事焰情,對開發(fā)人員來說要增加很多額外的工作量;

相信未來可能會(huì)通過強(qiáng)大硬件冗余來彌補(bǔ)的性能問題剥懒,讓開發(fā)人員專心做好業(yè)務(wù)内舟,而不用擔(dān)心性能問題;

我有這樣的觀點(diǎn)的原因是:不管怎樣初橘,科技的進(jìn)步都會(huì)以人為本验游,都是想給人類提供方便(原諒人類就是這么自私),開發(fā)人員當(dāng)然也是人了保檐,所以產(chǎn)生上述的觀點(diǎn)...

這部分內(nèi)容比較多耕蝉,稍微緩緩...

好,繼續(xù)回想一下上面的圖展东,灰色的view中有兩個(gè)按鈕赔硫,協(xié)議方法為了區(qū)分點(diǎn)了哪個(gè)按鈕,需要一個(gè)參數(shù)記錄點(diǎn)擊了哪一個(gè)按鈕盐肃,區(qū)分這個(gè)可以通過tag爪膊;

-(void)grayView:(LigthGrayView *)grayView didClickAtIndex:(NSUInteger)index;

為了提高代碼的可讀性,我們通常要不直接傳遞控件的tag砸王,而是定義枚舉推盛,然后將枚舉綁定到控件的tag上,用枚舉來消除魔法數(shù)字谦铃,增強(qiáng)代碼的可讀性耘成;多數(shù)情況下你最好這么干,因?yàn)樘O果的api中經(jīng)常這么干驹闰,如果你打算用枚舉瘪菌,請一定注意命名,命名不好的枚舉用起來讓人很不舒服嘹朗,你可不要小看命名這件事师妙,我記得有位計(jì)算機(jī)科學(xué)家說過:“在計(jì)算機(jī)科學(xué)中只有兩件難事:緩存和命名”,關(guān)于如何定義枚舉和命名這里就不再贅述屹培,實(shí)在不行默穴,看看蘋果在它的api中是如何使用和命名的怔檩,模仿它就不會(huì)有太大問題;


使用枚舉消除魔法數(shù)字后蓄诽,似乎代碼很漂亮薛训,很完美,符合規(guī)范仑氛;心里一陣開心‘我寫的代碼怎么就這么規(guī)范呢乙埃?’,就這樣我寫了一段時(shí)間的代碼后發(fā)現(xiàn)一件煩人的事情:

類似這個(gè)情況太多的時(shí)候调衰,代碼中定義了大量的協(xié)議膊爪,用了大量的代理,而為了可讀性我又寫了大量的枚舉嚎莉,有時(shí)候一個(gè)控制器遵循了若干協(xié)議米酬,每個(gè)協(xié)議都有需要實(shí)現(xiàn)的方法,代碼量就多了趋箩,而且結(jié)構(gòu)性不強(qiáng)赃额,方法分散,很多時(shí)候回頭review時(shí)叫确,忘了某個(gè)方法到底是哪個(gè)協(xié)議里的跳芳;時(shí)間久了,發(fā)現(xiàn)這其實(shí)是一件很沒有技術(shù)含量的體力活竹勉;而且多人開發(fā)的時(shí)候飞盆,有些開發(fā)人員并不會(huì)對枚舉命名嚴(yán)格要求,很多時(shí)候看到枚舉你還是不知道他是什么意思次乓,這是個(gè)現(xiàn)實(shí)問題...面對現(xiàn)實(shí)問題吓歇,我們要靈活的處理。

于是就思考能不能不用每次都寫協(xié)議票腰,不用每次都寫self.clickDelegate respondsToSelector someSel城看,畢竟我們處理的只是將點(diǎn)擊事件傳給控制器;
我的解決辦法

  • 定義一個(gè)公用協(xié)議杏慰,控制器監(jiān)聽內(nèi)部view的點(diǎn)擊事件都通過公用協(xié)議的方法测柠;
// view點(diǎn)擊事件的協(xié)議
@protocol YKViewClickProtcol <NSObject>
@optional
-(void)goActionWithName:(NSString*)strName withObject:(id)obj withSender:(id)sender;
@end

第一個(gè)參數(shù)strName用來區(qū)分點(diǎn)擊事件來自哪個(gè)子View,這個(gè)字符串可以使用類名傳遞的缘滥,因?yàn)轭惷谝粋€(gè)項(xiàng)目中是獨(dú)一無二的轰胁,也不用動(dòng)腦筋考慮命名問題;
第二個(gè)參數(shù)obj朝扼,有時(shí)候需要將數(shù)據(jù)傳給控制器软吐,方便做一些處理;
第三個(gè)參數(shù)sender用來區(qū)分子view中多個(gè)點(diǎn)擊事件吟税;
其實(shí)這個(gè)方法的定義不符合規(guī)范凹耙,如果看蘋果的代理方法,一條原則是代理方法的第一個(gè)參數(shù)是將源對象本身傳出去肠仪,這可能會(huì)是個(gè)問題肖抱;

  • 簡單的自定義一個(gè)UIView作為基礎(chǔ)view;
@interface YKView : UIView
@property (weak, nonatomic) id<YKViewClickProtcol> clickDelegate;
-(void)viewActionWithName:(NSString*)strName withObject:(id)obj withSender:(id)sender;
@end

YKView中定義一個(gè)和代理方法很像的方法,這個(gè)方法需要暴露在.h文件异旧,以供子類使用意述;
viewActionWithName... 方法的實(shí)現(xiàn)

-(void)viewActionWithName:(NSString*)strName withObject:(id)obj withSender:(id)sender
{
    if ([self.clickDelegate respondsToSelector:@selector(goActionWithName:withObject:withSender:)]) {
        [self.clickDelegate goActionWithName:strName withObject:obj withSender:sender];
    };
}

至此self.clickDelegate respondsToSelector ...在整個(gè)項(xiàng)目你只需要寫一遍即可;還有一個(gè)好處是控制器的代碼結(jié)構(gòu)會(huì)很強(qiáng)吮蛹,所有的子view點(diǎn)擊事件都在同一個(gè)地方處理-(void)goActionWithName:(NSString*)strName withObject:(id)obj withSender:(id)sender;
我也嘗試過更為極端的方式

// 獲取在你眼前的控制器
+(UIViewController *)getLastActivityController
{
    UIViewController *vController = [UIApplication sharedApplication].keyWindow.rootViewController;
    if ([vController isKindOfClass:[YKTabBarController class]]) {
        YKTabBarController *tabVc = (YKTabBarController *)vController;
        YKNavigationController *navVc = (YKNavigationController *)tabVc.selectedViewController;
        return navVc.visibleViewController;
    }
    
    if ([vController isKindOfClass:[YKNavigationController class]]) {
        YKNavigationController *navVc = (YKNavigationController *)vController;
        return navVc.visibleViewController;
    }
    return vController;
}

這個(gè)方法是用來獲取最上層(或者叫正在活動(dòng))的控制器荤崇,然后強(qiáng)行指定clickDelegate為此控制器,因?yàn)槔碚撋现v你能點(diǎn)到的view必定在最外層的控制器中潮针,這么干能少寫一條指定代理的賦值語句术荤;使用中也沒有出現(xiàn)錯(cuò)誤,但是有點(diǎn)極端了(點(diǎn)到為止即可)每篷,并不是沒次的點(diǎn)擊事件都需要傳遞給控制器處理瓣戚;


例子

Snip20150808_4.png

假設(shè)TestOneViewTestTwoView中的按鈕點(diǎn)擊事件都需要傳遞給控制器;

  • TestOneViewTestTwoView繼承YKView焦读,傳遞按鈕點(diǎn)擊事件只需要一行代碼子库;
- (IBAction)btnClick:(UIButton *)sender {
    [self viewActionWithName:NSStringFromClass([self class]) withObject:nil withSender:nil];
}
  • 可能你斷片了,把YKView中的處理代碼回憶一下矗晃。
-(void)viewActionWithName:(NSString*)strName withObject:(id)obj withSender:(id)sender
{
    if ([self.clickDelegate respondsToSelector:@selector(goActionWithName:withObject:withSender:)]) {
        [self.clickDelegate goActionWithName:strName withObject:obj withSender:sender];
    };
}
  1. ViewController處理代碼
- (void)viewDidLoad {
    [super viewDidLoad];

    CGFloat viewW = self.view.frame.size.width;

    TestOneView *oneV = [TestOneView oneView];
    oneV.clickDelegate = self;
    oneV.frame = CGRectMake(0, 74, viewW, 200);

    TestTwoView *twoV = [TestTwoView twoView];
    twoV.frame = CGRectMake(0, CGRectGetMaxY(oneV.frame) + 10, viewW, 200);
    twoV.clickDelegate = self;

    [self.view addSubview:oneV];
    [self.view addSubview:twoV];
}
#pragma mark- 子view點(diǎn)擊事件都在這里處理 -
-(void)goActionWithName:(NSString *)strName withObject:(id)obj withSender:(id)sender
{
    if ([strName isEqualToString:@"TestOneView"]) { 
        NSLog(@"oneView--didClick innerView");
    } else if ([strName isEqualToString:@"TestTwoView"]) {
        NSLog(@"TwoTwoView--didClick innerView");
    }
}

示例只是為了說明問題仑嗅,如果TestOneView中又多個(gè)事件交互需要傳遞,可以通過參數(shù)sender區(qū)分张症,需要傳遞其他數(shù)據(jù)可以通過obj仓技;

  • 代碼量少,能節(jié)省不少時(shí)間吠冤,不用總是定義協(xié)議浑彰,檢測代理,定義枚舉拯辙,一些沒技術(shù)含量的體力活郭变;
  • 事件處理在控制器中統(tǒng)一在一個(gè)方法,代碼不在松散涯保,review的時(shí)候能快速定位代碼诉濒;
    當(dāng)然可以對ViewController進(jìn)行封裝方便開發(fā),但是該結(jié)束本篇文章了夕春;

后記:

篇幅已然有點(diǎn)長了未荒,讀完文章,并不能像其他文章那樣看著demo快速寫出圖片折疊及志,絢麗的動(dòng)畫片排,或者寫出個(gè)二叉樹來寨腔;但是可能你的引發(fā)一些思考,...不對率寡,或許這篇文章毛也沒講出來迫卢;
那我就推薦幾本對我有很大幫助的書吧;

  • 《編寫高質(zhì)量iOS與OS X的代碼的52個(gè)有效方法》: 會(huì)讓你感覺自己之前不是在寫代碼冶共;
  • 《核心動(dòng)畫進(jìn)階》英文名: ios core animation advanced techniques:仔細(xì)研讀乾蛤,能寫出好多裝逼動(dòng)畫還有圖片的處理問題;
  • 《iOS進(jìn)階教程》- 唐巧寫的捅僵,估計(jì)你也買了家卖;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市庙楚,隨后出現(xiàn)的幾起案子趣兄,更是在濱河造成了極大的恐慌扬绪,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弧蝇,居然都是意外死亡驶鹉,警方通過查閱死者的電腦和手機(jī)谆焊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門蹲坷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人塞祈,你說我怎么就攤上這事金刁。” “怎么了议薪?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵尤蛮,是天一觀的道長。 經(jīng)常有香客問我斯议,道長产捞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任哼御,我火速辦了婚禮坯临,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恋昼。我一直安慰自己看靠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布液肌。 她就那樣靜靜地躺著挟炬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谤祖,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天婿滓,我揣著相機(jī)與錄音,去河邊找鬼泊脐。 笑死空幻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的容客。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼约郁,長吁一口氣:“原來是場噩夢啊……” “哼缩挑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鬓梅,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤供置,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后绽快,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芥丧,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年坊罢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了续担。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡活孩,死狀恐怖物遇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情憾儒,我是刑警寧澤询兴,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站起趾,受9級特大地震影響诗舰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜训裆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一眶根、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缭保,春花似錦汛闸、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春别伏,著一層夾襖步出監(jiān)牢的瞬間蹄衷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工厘肮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愧口,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓类茂,卻偏偏與公主長得像耍属,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子巩检,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,107評論 25 707
  • 吾家小女初長成厚骗, 始下庖廚表孝心。 賢內(nèi)治家將路引兢哭, 玉盞元宵報(bào)親恩领舰。
    荊楚日月閱讀 176評論 0 0
  • 方新細(xì)細(xì)端詳著剛進(jìn)門的女子,舉手投足都是戲迟螺,眼角眉梢都帶情冲秽,雖是一襲杏色裙衫,卻顯出撩人之色矩父,看上去身姿婀娜锉桑,美中...
    最可閱讀 497評論 2 1