淺談開發(fā)中提升工作效率的姿勢(shì)


簡介


回想起來,從畢業(yè)到現(xiàn)在在iOS這個(gè)行業(yè)也努(hua)力(shui)了好幾年,每每看到同事加班到深夜,于心不忍,故寫這篇博客,總結(jié)自己這幾年寫代碼的感悟,希望能幫助到那些加班到深夜的程序猿們.這篇博客主要有兩個(gè)主題,一是代碼規(guī)范,而是提升效率.雖然兩者看似風(fēng)牛馬不相及,但其中的聯(lián)系可是大大的存在,當(dāng)你注重了代碼規(guī)范,那么你的代碼質(zhì)量對(duì)應(yīng)的提升,反正,最終你加班的次數(shù)減少就對(duì)了~

文章也是隨意寫的,沒有什么順序,也就想到哪寫到哪,各位大佬就當(dāng)做飯后茶資來看吧~


正確的理解什么叫做寫代碼,理解業(yè)務(wù)邏輯的重要性


對(duì)于什么叫寫代碼,什么叫程序猿?首先來談?wù)勎易约簩?duì)寫代碼的觀點(diǎn),開發(fā)一個(gè)項(xiàng)目其中 50%-70%的工作量為理解業(yè)務(wù)邏輯,剩余的部分為編寫代碼,而在編寫代碼部分70%的工作量為處理異常情況,只有30%的工作量是開發(fā)程序.所以理解項(xiàng)目的業(yè)務(wù)邏輯是非常有必要的,因?yàn)闃I(yè)務(wù)邏輯決定UI和UE.例如,某個(gè)新增數(shù)據(jù)的Button 當(dāng)用戶沒有權(quán)限的時(shí)候,是不允許點(diǎn)擊的,當(dāng)你不管這些,允許沒有權(quán)限的用戶點(diǎn)擊操作該Button,那么就很有可能出現(xiàn)Bug,這是最常見的例子.所以,對(duì)于項(xiàng)目中業(yè)務(wù)邏輯,雖然不用做到倒背如流,但是最少要做到熟讀于心.


合理架構(gòu)代碼,提高工作效率


在寫代碼之前,一定要先去架構(gòu)自己的代碼結(jié)構(gòu),讓它盡量變的合理起來,靈活合理的代碼結(jié)構(gòu)會(huì)讓你更高效的工作,切忌先實(shí)現(xiàn),后優(yōu)化的理念去架構(gòu)代碼,有些程序猿(這幾年遇到不少)就是喜歡使用先實(shí)現(xiàn),后優(yōu)化的理念去架構(gòu)代碼,或者是連優(yōu)化都沒有,想到一種實(shí)現(xiàn)方式就立馬開始碼代碼,結(jié)果一堆Bug存在了自己寫的代碼里面.反正各種隱患,日積月累,Bug越來越多.到最后自己都不想去處理了,而且很多時(shí)候還是拆了東墻補(bǔ)西墻的情況,反正種種情況不斷. 下面我就分享一下我寫代碼的兩種架構(gòu)方式.

  • 正所謂業(yè)務(wù)邏輯決定代碼邏輯,所以我們可以通過業(yè)務(wù)邏輯來架構(gòu)我們的代碼結(jié)構(gòu).例如,現(xiàn)在業(yè)務(wù)邏輯中的帖子列表,只有展示和新增的邏輯,你就要立馬去想會(huì)不會(huì)在后面的版本有刪除或者修改的功能,或者還有分享的功能呢?是否需要給這些功能預(yù)留接口或者位置?用戶會(huì)不會(huì)有其他想法或者操作?每一種業(yè)務(wù)情景都可能對(duì)應(yīng)著Bug,架構(gòu)代碼之前多考慮業(yè)務(wù)邏輯是很有必要的.

  • 當(dāng)你需要修改某個(gè)代碼模塊的時(shí)候,這時(shí)候你也要先去思考當(dāng)你修改這個(gè)業(yè)務(wù)邏輯會(huì)不會(huì)對(duì)其他模塊造成影響,這里主要可以通過耦合性來去聯(lián)想其他模塊,然后去思考如何架構(gòu)代碼才能讓兼容性更好.這樣修改代碼是否會(huì)對(duì)后面的代碼迭代造成影響?

當(dāng)然了上面的只是簡單的舉例而已,有自己認(rèn)為合理的架構(gòu)方式歡迎評(píng)論.....??


合理復(fù)用代碼,業(yè)務(wù)邏輯代碼盡量復(fù)用,UI邏輯代碼少復(fù)用.


復(fù)用代碼,在很大程度上可以減少代碼的重復(fù)率,一個(gè)重復(fù)率很高的代碼工程不是一個(gè)合格的工程,所以,復(fù)用代碼是非常有必要的.

但是我們一定要去合理的復(fù)用代碼,不合理的復(fù)用代碼會(huì)造成的最常見問題就是代碼臃腫,耦合度高.例如,我們一個(gè)ViewController視圖控制器在UI的展現(xiàn)形式上在每一個(gè)地方都是一致的,但是每一個(gè)地方都需要不同的邏輯,有的是只展示,有的是既展示有可以跳轉(zhuǎn),有的是只跳轉(zhuǎn)不展示種種邏輯.如果我們都復(fù)用這個(gè)視圖控制器的話,那么這個(gè)視圖控制器的邏輯代碼會(huì)非常的多,各個(gè)使用這個(gè)控制器的模塊也會(huì)因此變得耦合度高了起來.

那么我們應(yīng)該遵循一個(gè)怎樣的復(fù)用規(guī)律呢?那就是業(yè)務(wù)邏輯代碼盡量復(fù)用,UI邏輯代碼少復(fù)用(PS:安卓的布局文件盡量復(fù)用,不涉及邏輯代碼,盡量復(fù)用).為什么這么說呢?這是因?yàn)闃I(yè)務(wù)邏輯決定著UI的展現(xiàn),業(yè)務(wù)邏輯發(fā)生改變,UI一般就發(fā)生了改變,相反,只要業(yè)務(wù)邏輯不發(fā)生改變,業(yè)務(wù)邏輯代碼也不需要發(fā)生改變.所以,業(yè)務(wù)邏輯代碼盡量多復(fù)用,例如網(wǎng)絡(luò)請(qǐng)求方法,我們寫在一個(gè)統(tǒng)一的文件中,誰用誰調(diào)用即可.只有當(dāng)后臺(tái)發(fā)生變化的時(shí)候,我們才需要修改代碼,大大的提高效率.


合理理解'閉環(huán)'現(xiàn)象,任何入口代碼在用戶使用過程中都需要出口代碼.


這里我稱之為'閉環(huán)'現(xiàn)象,也就是說任何入口代碼都需要出口代碼.當(dāng)然了,這是我的個(gè)人感覺,與其說這是代碼習(xí)慣,不如說它是我的一種思考習(xí)慣.而且我常常通過這種形式來完善我的代碼,比如我Push一個(gè)界面,我就會(huì)想到底有多少種方式Pop到上一個(gè)界面?每一種方式會(huì)不會(huì)有其他的分支情況等等,再例如用戶進(jìn)入了某個(gè)狀態(tài),怎么樣才能回到初始狀態(tài)?需不需要回到初始狀態(tài)(當(dāng)然,在想這種問題都是假設(shè)能回到初始狀態(tài),完成一個(gè)'閉環(huán)'現(xiàn)象.)等等, 還有就是下面寫到的if 和switch 的完整性問題,我也是常常用到這種思考方式,來驗(yàn)證我的代碼是否完整,一個(gè)不是'閉環(huán)'的代碼多多少少都會(huì)有點(diǎn)Bug.太深層次的我還沒有體驗(yàn),比如一個(gè)對(duì)象的創(chuàng)建必然會(huì)有對(duì)應(yīng)的銷毀過程,等等.


利用百度和Google解決日常問題和Bug.


程序猿日常開發(fā)過程中不免遇到這樣或者那樣的問題或者Bug,那么正確解決問題的姿勢(shì)是什么呢?

一般情況下,我會(huì)分下面幾步步驟操作.

  • 一悴势、回想自己以前是否遇到過類似問題或者Bug,自己的博客是否有記錄過這種問題(博客是程序猿很好的解決問題途徑).有沒有聽說過類似的問題.
  • 二、回想發(fā)現(xiàn)沒有該類似問題,那就思考問題可能出現(xiàn)的原因,仔細(xì)檢查自己的代碼邏輯,尋找問題可能出現(xiàn)的位置,打斷點(diǎn)驗(yàn)證正確性.
  • 三臂港、還是沒有發(fā)現(xiàn)問題,這時(shí)候,我們就要百度或者Google了,我們要把具體的問題盡量提取出關(guān)鍵字來查詢,提高查詢效率.比如,日志的錯(cuò)誤碼或者錯(cuò)誤信息等等,都是關(guān)鍵信息.
  • 四赏陵、其實(shí)上面的三步就已經(jīng)差不多把問題給解決了,但是還是有一些很具體的問題,怎么辦?我們要去回想我們身邊的大佬有沒有談及這塊的內(nèi)容,如果有,我們?nèi)ピ儐?盡量去詢問解決思路,而不是解決方法.比如,當(dāng)時(shí)我學(xué)習(xí)Java的時(shí)候,我就問當(dāng)時(shí)我們老大,我說'老大,有沒有相關(guān)的書籍或者學(xué)習(xí)網(wǎng)站呢?',而不是去問'老大,你教教我Java吧' 爾爾之語.最后想別人請(qǐng)教的時(shí)候,最好是有償?shù)?比如發(fā)個(gè)紅包什么的,數(shù)量不用太大,這樣做有兩個(gè)原因,一,讓別人知道你愿意為知識(shí)付費(fèi),這樣別人以后更喜歡幫助你.二,提醒自己,都TM是錢吶,別隨便去請(qǐng)教別人問題,自己動(dòng)手,豐衣足食.....

說一下反面教材,我曾經(jīng)碰到不止一個(gè)人問我問題,"你好,大佬,我這里有個(gè)問題,我把代碼發(fā)你,你給我看看吧",''大佬,可不可給我解決這個(gè)問題?(其實(shí)連文章都沒看,就讓我解決)"如此爾爾,還有很多的人覺得在工作中向別人提問問題是一種好學(xué)的體現(xiàn),但是我要說的是醒醒吧,你已經(jīng)不在學(xué)生時(shí)代了,醒醒吧你的老師已經(jīng)不在你身邊了,你去向別人提問問題,讓別人給你解決,就有可能是浪費(fèi)他的工作時(shí)間,來幫助你,那他的工作可能就完成不了,被老板罵是他,被老板噴是他.當(dāng)然了,對(duì)于騷棟自己而言,我還是很喜歡幫助別人的,只是不喜歡伸手黨而已.


善用 return 和 break 關(guān)鍵詞


returnbreak 代碼中常用的關(guān)鍵詞,其實(shí)還有一個(gè)關(guān)鍵詞continue,這里簡單的說明一下三者的作用以及不同之處.return是用來結(jié)束一個(gè)方法,break是來結(jié)束一個(gè)循環(huán)體,continue是來結(jié)束某個(gè)循環(huán)體中的一次循環(huán).

那么為什么要善于運(yùn)用 returnbreak 呢? 這主要是當(dāng)數(shù)組遍歷的時(shí)候,我們已經(jīng)尋找到了我們想要的數(shù)據(jù)的時(shí)候,我們就可以停止循環(huán)體,或者停止函數(shù)了,具體是選擇return 還是break ,要根據(jù)獲取到我們想要的數(shù)據(jù)后續(xù)是否還有操作來作為依據(jù).下面我們就舉例來說明.

例: 返回?cái)?shù)組中元素值為"test"的下標(biāo)(有且只有一個(gè)),并且組成"第x個(gè)元素為test"返回,沒有則返回nil

  • 在未做優(yōu)化代碼之前, 我們一般會(huì)想到我們要在循環(huán)體的外部創(chuàng)建一個(gè)字符串空對(duì)象,然后遍歷數(shù)組,找到符合條件的下標(biāo),組裝字符串,然后在循環(huán)體外返回.但是這樣做就會(huì)可能造成性能的浪費(fèi),比如要是數(shù)組元素個(gè)數(shù)為10個(gè),符合下標(biāo)的元素是在第一位,也就是說后面九次的循環(huán)都是毫無意義的,從而造成資源的浪費(fèi).
- (NSString *)returnThirdItemWithArray:(NSArray *)array {

    NSString *thirdItem = nil;
    for (int i = 0; i < array.count; i++) {
        NSString *item = array[i];
        if ([item isEqualToString:@"test"]) {
            thirdItem = [NSString stringWithFormat:@"第%d個(gè)元素為test",i + 1];
        }
    }
    return thirdItem;
}
  • 下面為優(yōu)化過后的代碼, 我們直接把return放在了if當(dāng)中,這樣當(dāng)在數(shù)組中找到合適的元素的時(shí)候就會(huì)立馬跳出函數(shù).不會(huì)有過多的性能浪費(fèi),我們要把握的時(shí)機(jī)就是只要當(dāng)函數(shù)滿足我們的需求時(shí)就停止函數(shù)的進(jìn)行即可.
- (NSString *)returnThirdItemWithArray:(NSArray *)array {

    for (int i = 0; i < array.count; i++) {
        NSString *item = array[i];
        if ([item isEqualToString:@"test"]) {
            return [NSString stringWithFormat:@"第%d個(gè)元素為test",i + 1];
        }
    }
    return nil;
}

break關(guān)鍵詞和上面的基本一致,主要是用于在當(dāng)前函數(shù)當(dāng)中跳出循環(huán)體時(shí)還需要做其他操作.這里就不多細(xì)說了.看例子吧~

//返回?cái)?shù)組中元素值為"test"的下標(biāo)(有且只有一個(gè)),并且組成"內(nèi)容為test的元素的下一個(gè)下標(biāo)為xxx".

  • 代碼優(yōu)化之前
- (void)findItemWithArray:(NSArray *)array {
    
    int index = 0;
    for (int i = 0; i < array.count; i++) {
        NSString *item = array[i];
        if ([item isEqualToString:@"test"]) {
            index = i + 1;
        }
    }
    NSLog(@"內(nèi)容為test的元素的下一個(gè)下標(biāo)為%d",index);
}
  • 代碼優(yōu)化之后
//返回?cái)?shù)組中元素值為"test"的第一個(gè)下標(biāo),并且組成"第x個(gè)元素為test"返回,沒有則返回nil
- (void)findItemWithArray:(NSArray *)array {
    
    int index = 0;
    for (int i = 0; i < array.count; i++) {
        NSString *item = array[i];
        if ([item isEqualToString:@"test"]) {
            index = i + 1;
            break;
        }
    }
    NSLog(@"內(nèi)容為test的元素的下一個(gè)下標(biāo)為%d",index);
}


關(guān)于 if 和 switch 產(chǎn)生 Bug 的思考


我可說在很多的初級(jí)小白百分之五十的Bug都是由于情況考慮不全導(dǎo)致的,那么在體現(xiàn)在代碼上是什么樣呢?在代碼上主要就是ifswitch寫的不完整造成情況考慮不全從而產(chǎn)生各種Bug.

我們先說一下if判斷語句,什么叫完整的if,什么叫不完整的if,如下代碼所示.

  • 不完整的if語句寫法
    if (條件) {
        操作
    }

    或者

    if (條件) {
        操作
    } else if (條件) {
        操作
    }
  • 完整的if語句寫法
    if (條件) {
        操作
    } else {
        操作
    }

    或者

    if (條件) {
        操作
    } else if (條件) {
        操作
    } else {
        操作
    }

關(guān)于完整性的if語句這種做法,很多書很多文章都稱之為if語句的窮舉法(自己看過<<Effective Objective -C 2.0>>中就有說到),也就是把if所有的情況都列舉出啦,哪怕它不需要任何的代碼操作.

<<Effective Objective -C 2.0>>PDF版?zhèn)魉烷T

對(duì)比上面的兩種if,很多看官又會(huì)說到,臥槽,你這是侮辱我智商呢?我剛剛學(xué)習(xí)編程就會(huì)了,只是后面為了方便,所以就不寫完整了,其實(shí)我工作以來也是基本上很少寫完整的if語句,能少些就少些,代碼同時(shí)整潔易懂.何樂而不為?但是要注意的是,在代碼層面上你可以不寫完整,但是你在心中一定要去把if語句的所有情況進(jìn)行窮舉,因?yàn)槊恳粋€(gè)if分支情況都可能是一個(gè)隱藏的Bug,這可能是業(yè)務(wù)邏輯方面的,也可能是代碼邏輯方面的.所以對(duì)if語句進(jìn)行窮舉操作是很有必要的.

那么對(duì)于switch是一樣的情況,switch中有default關(guān)鍵詞,很多時(shí)候,我們并不寫default部分,但是default部分也算是一個(gè)情況分支,這是我們所需要注意.但是有一種情況例外,那么就是switch的判斷條件為枚舉值的時(shí)候,這時(shí)候,情況總體個(gè)數(shù)已經(jīng)根據(jù)枚舉值的多少而定下了,所以不需要寫default部分了.


列表視圖能局部刷新絕對(duì)不全部刷新.


對(duì)于列表刷新是我們?nèi)粘i_發(fā)中最常見的一個(gè)操作,例如數(shù)據(jù)的刪除,新增,變動(dòng),修改等等都需要我們?nèi)ニ⑿铝斜?很多時(shí)候我們都是直接使用[self.mainTableView reloadData];來刷新數(shù)據(jù),但是我們仔細(xì)想想假定就只有一個(gè)或者有限的Cell需要刷新,你使用上面的那句話,那不是白白造成了許多的內(nèi)存資源浪費(fèi)嗎?所以我們能使用局部刷新絕對(duì)不使用全部刷新.

舉例子說明,iOS這邊我們能使用下面方法就使用下面方法進(jìn)行局部刷新,雖然在代碼量會(huì)有所提升,但是不會(huì)造成大量的資源浪費(fèi).

- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;

- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;


善用宏定義和枚舉,減少魔法數(shù)字的使用.


何為魔法數(shù)字?就是根本沒有任何的解釋,隨心所欲的寫在代碼之中的數(shù)字,反正就是讓人不明覺厲的那種就對(duì)了~ 魔法數(shù)字的危害性主要會(huì)體現(xiàn)在項(xiàng)目后期的維護(hù)上,在開發(fā)階段的時(shí)候,你根據(jù)隨手寫上了一個(gè)魔法數(shù)字,可能是寬高信息,可能是邊距信息,但是你沒有寫任何的注釋來表明這個(gè)數(shù)字是怎么來的,是做什么用的,我相信不出三個(gè)月,連你去看這個(gè)你當(dāng)前的魔法數(shù)字都會(huì)覺得很神秘.所以,在你的代碼中減少魔法數(shù)字扥出現(xiàn)是很有必要的.

那么如何去消除魔法數(shù)字這種危害性呢?

一,增加合理注釋,解釋這個(gè)魔法數(shù)字是如何產(chǎn)生的,在代碼當(dāng)中有著怎么樣的作用,雖然這樣可以在一定程度上解決了魔法數(shù)字的危害,注釋卻多的一皮,還有就是后期維護(hù)非常麻煩,假設(shè)很多的魔法數(shù)字分布在你的項(xiàng)目各個(gè)角落中,后期你要改的話,需要先去找到這個(gè)魔法數(shù)字的位置,然后再去修改,是你自己寫的代碼還好,如果是別人的代碼,光這個(gè)找的時(shí)間,就夠自己喝一壺的了~

二,既然使用注釋的方式不能完全解決魔法數(shù)字問題,我們就看一下使用宏定義和枚舉如何解決魔法數(shù)字問題.(其實(shí)當(dāng)某一種魔法數(shù)字少量的時(shí)候,使用注釋是完全可行的~,酌情而定)

  • 宏定義方式來定義魔法數(shù)字,全局都可能用到的魔法數(shù)字,我們就放在pch文件中,如果只是某幾個(gè)類可能用到的文件,我們就直接創(chuàng)建一個(gè).h文件,然后需要的導(dǎo)入即可,對(duì)于單個(gè)類使用的魔法數(shù)字宏定義,我們直接放在頭部即可.這樣的方式不但方便管理魔法數(shù)字,而且簡介明了,后期維護(hù)起來也是非常的方便.具體代碼示例如下所示.
//pch 文件中的全局宏定義
#define NavigationBarHeight  (44.0f)

#define TabBarHeight  (49.0f)

#define KNormalEdgeDistance  (16.0f)

#define KNormalViewDistance  (10.0f)
#ifndef HomeHeader_h
#define HomeHeader_h

//HomeHeader是所有帖子列表的所需信息主要包含內(nèi)容高度,Cell圖片部分的尺寸

//Cell左右邊距
#define EdgeDistance (15.0f * 4)

//Cell頂部邊距
#define TopEdgeDistance (15.0f)

//頭部分組信息高度
#define HeaderInfoHeight (27.0f)

#endif /* HomeHeader_h */
  • 再來說一下枚舉的問題.枚舉值也是很好的解決魔法數(shù)字的方式,注釋是用于狀態(tài)的展示,如果有兩種狀態(tài),我們一個(gè)布爾值就可以解決了,如果是多種狀態(tài),如果不用枚舉的話,到時(shí)候代碼中各種if (style == 1) {}等等魔法數(shù)字,完全讓人摸不到頭腦,各種翻文檔,各種翻接口找到對(duì)應(yīng)的業(yè)務(wù)意義.大大浪費(fèi)了時(shí)間.但是我們?nèi)绻嗣杜e類型了呢? 我們就可以快速的通過字面的意思推測(cè)出類型的意義,例如if (style == DrawStyleLine) {},我們可以清楚的明白我們的繪制的樣式為線性,定義的枚舉類型如下所示.
typedef enum : NSUInteger {
    DrawStyleLine,
    DrawStyleSquare,
    DrawStyleCircle,
    DrawStyleArrow,
    DrawStyleHand,
} DrawStyle;

當(dāng)然,宏定義和枚舉除了能解決魔法數(shù)字問題,還能解決書寫錯(cuò)誤問題,比如我們因?yàn)椴恍⌒陌?code>if (style == 1) {}寫成if (style == 10) {}在編譯過程中是沒有任何錯(cuò)誤的,只有在運(yùn)行過程中才可能暴露出其對(duì)應(yīng)的Bug來,但是我們?nèi)绻褂煤甓x或者枚舉,我們書寫不全,在編譯過程中就直接顯示錯(cuò)誤,例如把DrawStyleLine寫成DrawStyleLina,編譯器會(huì)直接提示我們書寫錯(cuò)誤,這樣也會(huì)有助于避免我們?cè)谶@些小問題上翻車.


多利用 位移枚舉 的位運(yùn)算實(shí)現(xiàn)業(yè)務(wù)邏輯中多選操作


我們經(jīng)常會(huì)在iOS中的.h看到這樣的枚舉,例如對(duì)于貝塞爾曲線的指定角進(jìn)行切邊操作,用到的枚舉類型,如下所示.

typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
    UIRectCornerTopLeft     = 1 << 0,
    UIRectCornerTopRight    = 1 << 1,
    UIRectCornerBottomLeft  = 1 << 2,
    UIRectCornerBottomRight = 1 << 3,
    UIRectCornerAllCorners  = ~0UL
};

這時(shí)候我們會(huì)發(fā)現(xiàn)枚舉類型的值并不是我們常見的0,1,2,3等等,而是1 << 0,1 << 2,1 << 3等等,如下圖所示,這代表著位運(yùn)行的表示形式, 示例解釋如下所示.

1 << 0 代表著 十進(jìn)制的 1 左移 0 位 那么就是 0001 (十進(jìn)制為1,具體運(yùn)算為1(2^0)),
1 << 1 代表著 十進(jìn)制的 1 左移 1 位 那么就是 0010 (十進(jìn)制為2,具體運(yùn)算為1
(2^1)),
1 << 2 代表著 十進(jìn)制的 1 左移 2 位 那么就是 0100(十進(jìn)制為4,具體運(yùn)算為1*(2^2)),

再來給各位小白惡補(bǔ)一下位運(yùn)算的幾種運(yùn)算符號(hào)的意義

位運(yùn)算的幾種常用運(yùn)算符號(hào)的意義
  • << 左移運(yùn)算符,就是將某一個(gè)整數(shù)的二進(jìn)制整體左移n位,例如 整數(shù)5(二進(jìn)制表示為0101)的位運(yùn)算 5 << 1,那么結(jié)果就是整數(shù)10 (二進(jìn)制為1010);
  • >> 右移運(yùn)算符,就是將某一個(gè)整數(shù)的二進(jìn)制整體右移n位,和左移運(yùn)算符類似.
  • & 按位與運(yùn)算符,只有對(duì)應(yīng)的兩個(gè)二進(jìn)位均為1時(shí),結(jié)果位才為1屡萤,否則為0, 例如5&9=1,解釋為0101&1001=0001,轉(zhuǎn)化成整數(shù)就是1.
  • | 按位或運(yùn)算符,只要對(duì)應(yīng)的二個(gè)二進(jìn)位有一個(gè)為1時(shí)歹茶,結(jié)果位就為1捎拯,否則為0, 例如5|9=13,解釋為0101|1001=1101,轉(zhuǎn)化成整數(shù)就是13.

那么說了這么多,位移枚舉的位運(yùn)算到底有什么的用途呢?其實(shí),這樣的枚舉任意幾個(gè)枚舉值相加的值(用其 按位或運(yùn)算即可~) 都是不一樣的,不信可以試驗(yàn)一下~我們也就是說可以對(duì)枚舉值的任意組合進(jìn)行判斷,我們就用UIRectCorner來說明一下,假設(shè)我們當(dāng)我們選擇的是UIRectCornerTopLeft和UIRectCornerTopRight的時(shí)候,我們就讓view的背景色為紅色,當(dāng)我們選擇的是UIRectCornerTopLeft和UIRectCornerBottomLeft我們就為黑色,其他的都為白色,示例如下.

if (value == UIRectCornerTopLeft|UIRectCornerTopRight) {
    view.backgroundColor = [UIColor redColor];
} else if (value == UIRectCornerTopLeft|UIRectCornerBottomLeft) {
    view.backgroundColor = [UIColor blackColor];
} else {
    view.backgroundColor = [UIColor whiteColor];
}

有人就會(huì)問我們用普通的枚舉來做多選會(huì)有什么問題,下面我來定義一個(gè)枚舉類型,大家來看一下,仍然用UIRectCorner來做說明.

// 錯(cuò)誤演示
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
    UIRectCornerTopLeft     = 1 ,
    UIRectCornerTopRight    = 2,
    UIRectCornerBottomLeft  = 3,
    UIRectCornerBottomRight = 4,
    UIRectCornerAllCorners  = 5
};

當(dāng)我們選擇 UIRectCornerTopLeft|UIRectCornerTopRight的時(shí)候計(jì)算出來的值為3,也就是說選擇 UIRectCornerTopLeft|UIRectCornerTopRight和選擇UIRectCornerBottomLeft是沒有任何區(qū)別的.因?yàn)槲覀兊呐袛嘁罁?jù)只能是枚舉所代表的值.這樣就會(huì)出現(xiàn)了問題,做不成多選操作,這種類型的枚舉只能來做單選操作.

當(dāng)然了,還是會(huì)有人比比用下面的例子說,這樣不是也能多選嗎?但是 1 就是 1 << 0, 2就是 1 << 1,其他的都是等同的,這里就不多比比了~

typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
    UIRectCornerTopLeft     = 1 ,
    UIRectCornerTopRight    = 2,
    UIRectCornerBottomLeft  = 4,
    UIRectCornerBottomRight = 8,
    UIRectCornerAllCorners  = ~0UL
};


合理理解高內(nèi)聚,低耦合 控制單個(gè)文件的代碼量


在上一家公司的時(shí)候,那時(shí)候的我還是那么天真單純,當(dāng)我接手iOS項(xiàng)目時(shí),再一次刷新了我的三觀,這是為什么呢?因?yàn)檫@個(gè)項(xiàng)目被上一伙人解耦解到支離破碎的,邏輯分散的各個(gè)角落中了,簡直是慘不忍睹.最后一問原來是有后臺(tái)開發(fā)大佬參與了開發(fā)~ 后來我接觸了Java后臺(tái),我才明白為什么會(huì)寫的支離破碎,在Java的前后端不分離的webApp中,View和Controller就是完全分離的~但是在iOS中,View和Controller的邏輯在一定程度上是內(nèi)聚的.當(dāng)然了,埋怨當(dāng)時(shí)的后臺(tái)開發(fā)人員,畢竟每一種編程語言都有一定的規(guī)則.

好了,言歸正傳,我們來說說高內(nèi)聚,低耦合的問題,高內(nèi)聚,低耦合這個(gè)概念我相信在學(xué)編程之初,你的老師就一定提過,高內(nèi)聚就是讓我們要把相關(guān)度比較高的部分盡可能的集中,不要分散.但是一旦過分高內(nèi)聚,就會(huì)造成代碼臃腫不堪,業(yè)務(wù)邏輯混亂復(fù)雜的情況,而低耦合就是讓我們把兩個(gè)相關(guān)的模塊盡可以能把依賴的部分降低到最小,不要讓兩個(gè)系統(tǒng)產(chǎn)生強(qiáng)依賴.但是如果過度低耦合,那么就會(huì)造成上面的那種情況,代碼邏輯支離破碎,代碼可讀性非常差.所以具體的高內(nèi)聚,低耦合的概念如何在你的代碼中體現(xiàn),是需要一定的編程經(jīng)驗(yàn)的~ 當(dāng)然,高內(nèi)聚低耦合這個(gè)概念的標(biāo)準(zhǔn),什么時(shí)候該內(nèi)聚,什么該解耦,在每一個(gè)程序猿眼里,我相信都是不一樣,有的人認(rèn)為這個(gè)部分應(yīng)該解耦,認(rèn)為邏輯堆在這里過于臃腫,但是有的人卻認(rèn)為這里的代碼根據(jù)業(yè)務(wù)邏輯就應(yīng)該堆在這里,可以提高代碼的可讀性,所以這個(gè)標(biāo)準(zhǔn)是只可意會(huì)不可言傳的,哈哈.只要心中有這個(gè)概念,不用刻意去追求,水到渠成即可~

通過內(nèi)聚和解耦,我們可以合理的控制單個(gè)代碼文件的代碼量,其實(shí)我不建議一個(gè)代碼文件中的代碼量太多.這樣會(huì)造成代碼非常的臃腫,可讀性也是很差的.比如我以前寫過一個(gè)列表的九宮格Cell(每一種情況都是一個(gè)新的UI),里面的代碼超過兩千多行,著實(shí)是臃腫不堪~維護(hù)起來非常的麻煩.這時(shí)候,我們就可以把部分的代碼抽出來,寫在一個(gè)新的文件中.當(dāng)然了,如果實(shí)在是解耦不了,我們一定要去加注釋,注明這里的代碼是干什么用的,為什么要這么做等等,為后期維護(hù)或者二次開發(fā)做好鋪墊工作.


合適使用注釋 ,相對(duì)于“做了什么”毯欣,更應(yīng)該說明“為什么這么做”


代碼千萬行馒过,注釋第一行;編程不規(guī)范酗钞,同事兩行淚
通過上面的詩句,我們就可以深刻的體會(huì)到注釋的重要性,從我們開始寫第一行的代碼時(shí)候,很多的大佬就會(huì)教導(dǎo)我們一定要把注釋寫好,寫明白,我想大多數(shù)程序猿會(huì)有這種感覺,如果不寫注釋,當(dāng)我們自己會(huì)看自己三個(gè)月前的代碼時(shí),我們都會(huì)大聲的說一句,"我擦,這是寫的什么鬼~ ,這肯定不是我寫的",所以寫注釋,寫好注釋,這是一種利人利己的行為,何樂為不為?網(wǎng)上很多有很多寫注釋的重要性的博客或者文章,我想都是深受其害的程序猿~

在寫注釋的時(shí)候,既不能像大姨媽一樣拖拖拉拉的寫一堆,也不能為了成為衛(wèi)生標(biāo)兵就一點(diǎn)也不做注釋,合理的注釋會(huì)讓你的代碼可讀性更高(其實(shí),就算你把注釋注的再詳細(xì),我想別人也不愿意去看你寫的代碼,通病而已,??),我們?cè)趯懽⑨尩臅r(shí)候,不但要寫明這代碼是干什么用的,有時(shí)候更應(yīng)該寫為什么要去這么寫,你當(dāng)時(shí)所想的想法是什么,比如有個(gè)計(jì)算Cell的高度的時(shí)候,由于里面可能有固定高度也可能有可變高度,我一般會(huì)在Cell的.h文件中如下圖進(jìn)行類似注釋(雖然這個(gè)類是用來做tableViewHeaderView的,但是是一樣的.).詳細(xì)的注明,Cell的高度是怎么來的,哪怕是日后再也看這些代碼也是很輕松的.要不然,真的就成了"編程不規(guī)范腹忽,同事兩行淚"了.


利用 狀態(tài)機(jī) 和 枚舉 完成一個(gè)控制器多種狀態(tài)UI展示效果


狀態(tài)機(jī)的這個(gè)概念我第一次接觸是在Untiy 3D 做游戲用到的,其實(shí)就是一個(gè)監(jiān)控狀態(tài)流轉(zhuǎn)的模塊,例如,一個(gè)人有三種操作,一個(gè)是停止吃喝,一個(gè)是吃飯,一個(gè)是喝水.我們可以讓一個(gè)人從吃飯到停止吃喝,或者從停止吃喝到吃飯,但是我們不能讓一個(gè)人吃飯直接切換到喝水.因?yàn)槲覀円V沽顺燥?再去喝水,當(dāng)然了,你非要一手吃飯一手喝水也行~ 請(qǐng)出門右轉(zhuǎn)不送.狀態(tài)的流轉(zhuǎn),什么樣的狀態(tài)可以流轉(zhuǎn)到什么狀態(tài),不可以流轉(zhuǎn)到什么樣的狀態(tài),我們做一總結(jié),這就是初步的狀態(tài)機(jī).

當(dāng)我們?cè)谝粋€(gè)頁面中我們有多種UI要展示,而我們要放在同一個(gè)控制器中,那么我們就要考慮狀態(tài)的流轉(zhuǎn)了,其實(shí)也就是狀態(tài)機(jī)的問題.如果不做好狀態(tài)的流轉(zhuǎn),可能會(huì)導(dǎo)致邏輯代碼分散到各個(gè)位置,反正就是一個(gè)字,亂.

這時(shí)候,我們要先去理清UI的狀態(tài)是如何流轉(zhuǎn)的,然后我們要定義枚舉,有多少種狀態(tài)就定義多少種枚舉值.例如我做過的一個(gè)關(guān)于Socket的項(xiàng)目中就有一個(gè)界面根據(jù)socket的不同狀態(tài)需要展示不同的UI,枚舉代碼如下所示.

typedef enum : int {
    ConnectedStateWIFINotConnect,//wifi還未連接
    ConnectedStateWIFIContented,//wifi已連接
    ConnectedStateSocketConnecting,//socket連接中
    ConnectedStateSocketNotConnect,//socket連接失敗
    ConnectedStateSocketContented,//socket已經(jīng)連接
    ConnectedStateSocketDisconnect//socket斷開連接
} ConnectedState;

接著,我們需要用switch 來做狀態(tài)的流轉(zhuǎn)之后的邏輯代碼, 有人說為什么不用if else做,我不多說,自行體會(huì)去.部分代碼(已經(jīng)做了刪減了)如下所示.

- (void)loadSubViewStateAction {
    
    // 刪除所有的子控件,然后重新添加,我用的是懶加載的形式,所以不太用考慮性能問題.
    [self.view.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];

    switch ([SocketClinetManager defaultManager].connectedState) {
            
        case ConnectedStateWIFINotConnect:{
            // 當(dāng)前WIfI未連接  分為未綁定盒子和已綁定盒子
            if ([UserManager defaultManager].connectBoxName == nil) {
                [self.view addSubview:self.bindButton];
            } else {
                [self.view addSubview:self.reloadConnectButton];
                [self.view addSubview:self.boxConnectWifiView];
            }
        }break;
            
        case ConnectedStateWIFIContented:{
            // 當(dāng)前WIFI已連接
            _socketContentInfoLabel.text = @"設(shè)備未連接";
            [self.view addSubview:self.connectBoxButton];
            [self.view addSubview:self.reloadBindButton];
        }break;
            
        case ConnectedStateSocketConnecting:{
            _socketContentInfoLabel.text = @"連接中...";
            [self.view addSubview:self.reloadBindButton];
            [self.view addSubview:self.reloadConnectButton];
            [self socketStateImageViewStartAnimationAction];
        }break;
            
        case ConnectedStateSocketNotConnect:{
            // socket未連接
            _socketContentInfoLabel.text = @"設(shè)備連接失敗";
            [self.view addSubview:self.reloadBindButton];
            [self.view addSubview:self.reloadConnectButton];
        } break;
        case ConnectedStateSocketContented:{
            // scoket連接成功
            _socketContentInfoLabel.text = @"連接狀態(tài)正常";
            [self.view addSubview:self.reloadBindButton];
            [self.view addSubview:self.disconnectButton];
        }break;

        case ConnectedStateSocketDisconnect:{
            _socketContentInfoLabel.text = @"設(shè)備未連接";
            [self.view addSubview:self.connectBoxButton];
        }break;
    }
}

然后我們只需要修改[SocketClinetManager defaultManager].connectedState的值,然后調(diào)用loadSubViewStateAction這個(gè)方法就可以得到我們所需要的UI了.其實(shí),這個(gè)模塊算的上是一個(gè)開發(fā)經(jīng)驗(yàn)吧,如果有這種需要可以用上這種模式,這樣寫我個(gè)人感覺把UI狀態(tài)流轉(zhuǎn)放到一個(gè)地方更方便去管理,在代碼的可讀性上也會(huì)有更大的提高.


合理使用懶加載.尤其是在 removeFromSuperView時(shí)候,就是兩字,真香.


按需加載,是優(yōu)化代碼的一個(gè)重要的途徑.先說說我自己吧,寫了這么多年,一直在使用懶加載的形式創(chuàng)建控件,其實(shí)在那些視圖要出現(xiàn)的就需要加載完成的控件身上,懶加載并沒有提高什么效率,反而讓代碼量上升了,這種情形最好的好處也就是代碼規(guī)范整潔,不用所以的控件初始化都擠在一個(gè)方法里面,其他別無用途.但是當(dāng)我們有彈窗這種用戶主觀調(diào)出的視圖的時(shí)候,我們就可以用懶加載的形式,這樣當(dāng)用戶需要的時(shí)候,我們才去分配內(nèi)存,初始化控件,不用在父類初始過程中就要分配內(nèi)存空間,降低了程序內(nèi)存峰值,提高了效率.

但是我要說的時(shí)候,什么時(shí)候使用懶加載是最爽的?那就是當(dāng)Cell中有個(gè)控件,有的數(shù)據(jù)需要展示,有的數(shù)據(jù)則不需要展示,我們?cè)谂浜仙舷聞澗€直接訪問屬性的形式,就是兩字,真香.

說的再多,也可能是白扯,我們看一下例子~ 這里有一個(gè)班級(jí)選擇列表,當(dāng)用戶選擇某個(gè)班級(jí)的時(shí)候,后面才會(huì)會(huì)出現(xiàn)選中按鈕,否則不會(huì)出現(xiàn)選中按鈕,如下圖所示.

Cell中部分代碼如下所示.初始化過程中不做任何操作,懶加載還是平常的懶加載.


// 初始化過程中不做任何操作
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        self.selectionStyle = UITableViewCellSeparatorStyleNone;
    }
    return self;
}

//選中圖片的懶加載
- (UIImageView *)selectImageView {
    
    if (_selectImageView == nil) {
        _selectImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"common_class_select_icon"]];
        _selectImageView.frame = CGRectMake(KmainWidth - 48.0f, 0, 48.0f, 48.0f);
        _selectImageView.contentMode = UIViewContentModeCenter;
    }
    return _selectImageView;
}

但是在賦值的時(shí)候就是提高性能的時(shí)候,我們的想法是Cell上是否含有圖片控件,我們都刪除.然后重新添加圖片控件,這就是我們?yōu)槭裁丛谶@里使用的是_selectImageView的原因,_selectImageView可以不通過set方法直接訪問成員屬性,所以假設(shè)圖片控件沒有被創(chuàng)建,那么就是[nil removeFromSuperview]了,為什么不用[self.selectImageView removeFromSuperview];呢?因?yàn)橐坏?strong>self.selectImageView就會(huì)調(diào)用get方法從而創(chuàng)建控件,懶加載也就失去了意義.通俗講法就是,刪除控件的時(shí)候,控件沒有初始化我就不進(jìn)行刪除,如果有,那么我就進(jìn)行刪除控件操作.從而提高代碼效率.

//賦值數(shù)據(jù)時(shí)
- (void)setDataModel:(ClassModel *)dataModel {
    
    _dataModel = dataModel;
    
    [_selectImageView removeFromSuperview];

    if (dataModel.isSelect) {
        [self.contentView addSubview:self.selectImageView];
    }
}


及時(shí)解決代碼冗余問題


代碼冗余這種問題說大不大,說小不小,代碼冗余每一個(gè)工程都或多或少有這樣的問題,其實(shí)冗余的代碼在業(yè)務(wù)邏輯上并不會(huì)有太大的影響,但是在后期代碼維護(hù)上是存在著一定的問題,一定程度上增加了閱讀難度,所以當(dāng)你發(fā)現(xiàn)自己的代碼冗余的時(shí)候,一定要及時(shí)刪除冗余的代碼.


學(xué)習(xí)并使用優(yōu)秀的三方組件,嘗試自己封裝一些侵入性低的組件


首先說明一點(diǎn),雖然以前的我造過不少的輪子,我不太提倡在工作中重復(fù)的去造輪子,這主要是因?yàn)樽约涸斓妮喿涌赡苡捎陂_發(fā)時(shí)間過短會(huì)存在各種問題或者Bug.而且還浪費(fèi)你的工作時(shí)間,降低了工作效率.很多的優(yōu)秀的三方迭代的較多,所以穩(wěn)定性很好,如果你在工作中需要某一個(gè)三方,我建議先去網(wǎng)上找找看,如果有合適的,盡量使用那么穩(wěn)定的三方來解決工作中的問題.提高自己的工作效率,這樣就不用天天加班到深夜了.

很多優(yōu)秀的三方組件都是值得我們?nèi)W(xué)習(xí)的,我們可以通過查看組件源碼的形式學(xué)習(xí)開發(fā)者當(dāng)時(shí)的開發(fā)邏輯,看多了,自然也就懂了.

當(dāng)然了,在我們業(yè)余的時(shí)間,我們可以去嘗試封裝一些入侵性較低的組件,下面有侵入性的解釋,侵入性就伴隨著耦合問題,所以在封裝組件的時(shí)候,組件的侵入性是一個(gè)很好的衡量組件優(yōu)劣的方式.

當(dāng)你的代碼引入了一個(gè)組件,導(dǎo)致其它代碼或者設(shè)計(jì),要做相應(yīng)的更改以適應(yīng)新組件.這樣的情況我們就認(rèn)為這個(gè)新組件具有侵入性.


做好釋放工作,完成"閉環(huán)"現(xiàn)象


當(dāng)一塊內(nèi)存被分配的時(shí)候,你就需要想這塊內(nèi)存需不需要你自己來釋放它(也就是上面提到的"閉環(huán)"現(xiàn)象).當(dāng)你確定這塊內(nèi)存需要你來釋放,不妨提前寫好釋放代碼,防止自己遺忘.大大減少因?yàn)閮?nèi)存釋放問題所造成的Bug或者問題的數(shù)量.反正牢記"創(chuàng)建就要銷毀"的理念就行了.


不炫技,簡單易懂才是最屌的代碼


自己學(xué)到了某一個(gè)新的框架或者組件,總是想著把它使用到我們的項(xiàng)目當(dāng)中,雖然這樣是沒有問題的,但是我們忽略了它的穩(wěn)定性和可讀性,一個(gè)新的框架可能會(huì)存在很多的問題或者Bug,所以代碼的穩(wěn)定性是一個(gè)很大的問題,再加上當(dāng)別人來接手你的代碼時(shí),很有可能因?yàn)檫@些新的框架而需要額外的學(xué)習(xí)時(shí)間,從而造成了工作效率的降低,這都是一些潛在的風(fēng)險(xiǎn).


保持函數(shù)的功能單一性,控制每個(gè)函數(shù)的代碼量


我們?cè)跇?gòu)建一個(gè)函數(shù)之前,我們要思考這個(gè)函數(shù)到底在我們程序中扮演著什么樣的功能模塊,從而保持函數(shù)的功能單一性,從代碼結(jié)構(gòu)上來說,一個(gè)功能單一的函數(shù)更利于閱讀,同時(shí),由于我們需要保持每個(gè)函數(shù)的功能單一性就必然會(huì)去抽離代碼,重新組裝新的函數(shù),這樣每一個(gè)函數(shù)的代碼量都不會(huì)有太多.閱讀起來相當(dāng)?shù)妮p松.

例如,我寫的自定義UITableViewCell都是通過懶加載的方式抽離出代碼,如下圖所示,這樣的代碼層次感就出現(xiàn)了,使人更容易理解代碼邏輯.而不是把所有的控件初始化都放在init方法中,如果這樣做的話,雖然沒有任何的問題,但是到底是什么控件添加了什么控件就需要仔細(xì)閱讀代碼了,增加了閱讀的難度.


淺談NSTimer的釋放問題


很多iOS初級(jí)開發(fā)者在使用NSTimer做定時(shí)器功能的時(shí)候,往往一個(gè)不消息就會(huì)造成了內(nèi)存泄露問題,當(dāng)然了,這也包括我在內(nèi),我們來舉例說明一下NSTimer的釋放問題.

首先,我們來舉例子說明一下NSTimer的循環(huán)引用.首先我們?cè)赩iewController分類中定義一個(gè)NSTimer的成員變量.如下所示.

#import "ViewController.h"

@interface ViewController ()

@property(nonatomic,strong)NSTimer *timer;

@end

然后我們?cè)谙旅娴膁elloc中釋放該NSTimer對(duì)象.

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}

- (void)timerAction {
    
}

- (void)dealloc {
    
    if (_timer != nil) {
        [_timer invalidate];
        _timer = nil;
    }
}

@end

然后我們就會(huì)發(fā)現(xiàn)dealloc方法根本不走,也就是說我們釋放不了這個(gè)NSTimer對(duì)象,這樣就循環(huán)引用了 ,然后有人就說,你用strong強(qiáng)引用NSTimer對(duì)象了.所以釋放不了,但是我要告訴就算我改成下面哪種方式,循環(huán)引用依然是存在的.

@interface ViewController ()

@property(nonatomic,weak)NSTimer *timer;

@end
@interface ViewController ()
{
    NSTimer *timer;
}

@end

那么NSTimer循環(huán)問題到底出現(xiàn)在哪里呢?這個(gè)循環(huán)引用的根源是在Target上,其實(shí)NSTimer的target參數(shù)會(huì)被RunLoop所持有(此時(shí)ViewController對(duì)象引用計(jì)數(shù)為2),也就是說銷毀界面的時(shí)候,ViewController對(duì)象引用計(jì)數(shù)依然是1,故不能被釋放,也就不能走dealloc方法.所以造成了循環(huán)引用.

既然知道了問題所在,我們只要打破環(huán)中一個(gè)位置即可,這里常見的方式就是在用戶主動(dòng)調(diào)用的方法中釋放NSTimer,先釋放NSTimer,然后RunLoop釋放了對(duì)ViewController對(duì)象的持有,ViewController對(duì)象的引用計(jì)數(shù)變?yōu)?,然后銷毀界面ViewController對(duì)象的引用計(jì)數(shù)變成0,對(duì)象被成功銷毀,如下所示.

//假設(shè)是導(dǎo)航控制器 Push的界面
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    if (_timer == nil) {
        [_timer invalidate];
        _timer = nil;
    }
    [self.navigationController popViewControllerAnimated:YES];
}

// dealloc不做操作
- (void)dealloc {

}

在iOS 10 出現(xiàn)了一個(gè)新的NSTimer構(gòu)建方法,那就是使用block的形式,雖然能解決上的問題,但是依然需要注意block中self的循環(huán)引用問題,具體方法如下圖所示.

總結(jié)NSTimer釋放秘訣就是下面的這句話.

通過用戶主動(dòng)操作調(diào)用的方法中來釋放NSTimer,任何時(shí)候都不要在dealloc中釋放NSTimer.


不建議在if中添加過長的判斷語句,如果需要,那么就分行顯示,提高代碼的可讀性


if分支語句中的判斷條件有時(shí)候很多,如下所示.如果我們順著寫,代碼不但臃腫了~ 閱讀起來及其的不方便.這時(shí)候我們就可以使用分行顯示的形式,來展示我們的判斷條件.提高閱讀效率.如下所示.

        // 未優(yōu)化之前
        if ([test isEqualToString:@"條件1"] || [test isEqualToString:@"條件2"] || [test isEqualToString:@"條件3"] ) {
            
        }
        // 優(yōu)化之后
        if ([test isEqualToString:@"條件1"] ||
            [test isEqualToString:@"條件2"] ||
            [test isEqualToString:@"條件3"]) {
            
        }

當(dāng)然假設(shè)某一個(gè)條件過長的時(shí)候,我們也可以利用抽離的方式,讓代碼看起來整潔大方.具體例子如下所示.

        // 未優(yōu)化之前
        if ([test isEqualToString:@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] ||
            [test isEqualToString:@"條件2"] ||
            [test isEqualToString:@"條件3"]) {
            
        }
        // 優(yōu)化之后

        BOOL firstCondition = [test isEqualToString:@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"];
        if (firstCondition ||
            [test isEqualToString:@"條件2"] ||
            [test isEqualToString:@"條件3"]) {
            
        }


代碼風(fēng)格要規(guī)范統(tǒng)一,做到自己現(xiàn)有水平的最好標(biāo)準(zhǔn).


自己的代碼一定要有統(tǒng)一的風(fēng)格,一個(gè)良好的代碼風(fēng)格是一個(gè)程序猿最基本的要求,一個(gè)良好的代碼風(fēng)格可以讓別人在閱讀自己寫的代碼時(shí)候更加輕松.萬萬不可在每一個(gè)代碼文件中都有著不同的風(fēng)格,這樣在閱讀代碼的時(shí)候肯定是非常難受的.寫代碼的時(shí)候我們要做到自己現(xiàn)在水平的最好,要像對(duì)待自己的孩子一樣對(duì)待自己的代碼,你只有呵護(hù)它,它更好的才能回報(bào)于你.下面我們就談一下幾種常見的代碼規(guī)范.可以稍微參考.

參考于<<如何提高代碼的可讀性? - 讀《編寫可讀代碼的藝術(shù)》>>

  • 命名規(guī)范問題


命名盡量使用駝峰命名法,命名的時(shí)候不可隨意取名,例如 action1 ,盡量要做到見名知意.


我們知道駝峰命名可以很清晰地體現(xiàn)變量的含義,但是當(dāng)駝峰命名中的單元超過了3個(gè)之后砚作,就會(huì)很影響閱讀體驗(yàn):
userFriendsInfoModel
memoryCacheCalculateTool
是不是看上去很吃力窘奏?因?yàn)槲覀兇竽X同時(shí)可以記住的信息非常有限,尤其是在看代碼的時(shí)候葫录,這種短期記憶的局限性是無法讓我們同時(shí)記住或者瞬間理解幾個(gè)具有3~4個(gè)單元的變量名的着裹。所以我們需要在變量名里面去除一些不必要的單元.(PS:這一點(diǎn)我還真沒做到....??)


不能使用大家不熟悉的縮寫
有些縮寫是大家熟知的:
doc 可以代替document
str 可以代替string
但是如果你想用BEManager來代替BackEndManager就比較不合適了。因?yàn)椴涣私獾娜藥缀跏菬o法猜到這個(gè)名稱的意義的米同。
所以類似這種情況不能偷懶骇扇,該是什么就是什么,否則會(huì)起到相反的效果面粮。因?yàn)樗雌饋矸浅D吧傩ⅲ覀兪熘囊恍┛s寫規(guī)則相去甚遠(yuǎn)。


  • 提高代碼的美觀性


在聲明一組變量的時(shí)候熬苍,由于每個(gè)變量名的長度不同稍走,導(dǎo)致了在變量名左側(cè)對(duì)齊的情況下,等號(hào)以及右側(cè)的內(nèi)容沒有對(duì)齊:

NSString *name = userInfo[@"name"];
NSString *sex = userInfo[@"sex"];
NSString *address = userInfo[@"address"];

而如果使用了列對(duì)齊的方法,讓等號(hào)以及右側(cè)的部分對(duì)齊的方式會(huì)使代碼看上去更加整潔:

NSString *name    = userInfo[@"name"];
NSString *sex     = userInfo[@"sex"];
NSString *address = userInfo[@"address"];

這二者的區(qū)別在條目數(shù)比較多以及變量名稱長度相差較大的時(shí)候會(huì)更加明顯钱磅。


當(dāng)涉及到相同變量(屬性)組合的存取都存在的時(shí)候梦裂,最好以一個(gè)有意義的順序來排列它們:

  • 讓變量的順序與對(duì)應(yīng)的HTML表單中<input>字段的順序相匹配
  • 從最重要到最不重要排序
  • 按照字母排序

舉個(gè)例子:相同集合里的元素同時(shí)出現(xiàn)的時(shí)候最好保證每個(gè)元素出現(xiàn)順序是一致的。除了便于閱讀這個(gè)好處以外盖淡,也有助于能發(fā)現(xiàn)漏掉的部分年柠,尤其當(dāng)元素很多的時(shí)候:

//給model賦值
model.name    = dict["name"];
model.sex     = dict["sex"]褪迟;
model.address = dict["address"]冗恨;

 ...
  
//拿到model來繪制UI
nameLabel.text    = model.name;
sexLabel.text     = model.sex;
addressLabel.text = model.address;


有些時(shí)候,你的某些代碼風(fēng)格可能與大眾比較容易接受的風(fēng)格不太一樣味赃。但是如果你在你自己所寫的代碼各處能夠保持你這種獨(dú)有的風(fēng)格掀抹,也是可以對(duì)代碼的可讀性有積極的幫助的。

比如一個(gè)比較經(jīng)典的代碼風(fēng)格問題:

if(condition){

}

or:

if(condition)
{

}

對(duì)于上面的兩種寫法心俗,每個(gè)人對(duì)條件判斷右側(cè)的大括號(hào)的位置會(huì)有不同的看法傲武。但是無論你堅(jiān)持的是哪一個(gè),請(qǐng)?jiān)谀愕拇a里做到始終如一城榛。因?yàn)槿绻心硯讉€(gè)特例的話揪利,是非常影響代碼的閱讀體驗(yàn)的。


熟悉常用的顏色以及顏色的表示方式


作為一個(gè)程序猿,日常開發(fā)中在UI方面說的最多的可能就是"RGB值","#f5f5f5","RGBA"等等,每一天都和各種各樣的顏色打交道,但是我見過很多的程序猿對(duì)顏色這塊的知識(shí)實(shí)在太少了,今天,我們就簡單地聊聊關(guān)于顏色一些常識(shí).

而我們?cè)谌粘V蠻I給我們最多的就是RGB值表示顏色,例如下下圖所表示的 "#F500CC",那么其中F5代表著紅色的十六進(jìn)制值,00代表綠色的十六進(jìn)制值,CC代表著藍(lán)色的十六進(jìn)制值,我們知道十六進(jìn)制是從0到F,所以兩位的十六進(jìn)制最大值為16*16 = 256 (十進(jìn)制),這也就是256顏色值的來由.三者全都是256的值那就是#FFFFFF(白色),三者都是0的值那就是#000000(黑色).

那么,現(xiàn)在我們就可以創(chuàng)建一個(gè)簡單的顏色,比如我只想要紅色,那么我們就讓紅色的值不為0,然后綠色和藍(lán)色的值都是0即可,比如#FF0000,就是最滿的紅色,當(dāng)紅色的值變小時(shí),顏色逐漸趨于黑色,我們可以通過下面來來了解這種變化.

再例如,我們可以通過本模塊的第一個(gè)圖來調(diào)出黃色,黃色就是紅色加上綠色,那么RGB十六進(jìn)制表示方式就為#FFFF00.其他的顏色以此類似.

我們接下來說一個(gè)比較有意思的顏色,那就是灰色,很多專業(yè)屬于稱之為中性灰,灰色是怎么來的呢?灰色其實(shí)就是RGB三個(gè)值是一樣的即可. 例如 #C0C0C0 , #F5F5F5等等,只有是#ABABAB或者#AAAAAA的形式都是中性灰.這里還要說一個(gè)中性灰叫做 #808080 ,很多人稱之為絕對(duì)中性灰.


寫博客是一個(gè)快速提高的姿勢(shì)


從我剛開始工作開始,我就一直在寫博客,雖然也是中間也是斷斷續(xù)續(xù),但是我絕對(duì)要說寫博客是讓一個(gè)程序猿快速成長的良好途徑.當(dāng)然了,我說的寫博客并不是讓你去抄網(wǎng)上的博客,一頓CV完了之后就沒事了,寫博客主要是寫自己的學(xué)習(xí)代碼和記錄問題,而不是去抄襲,就算是抄襲同一個(gè)問題,你也要加上自己的觀點(diǎn),談?wù)勀銓?duì)這個(gè)問題的理解.而不是抄一頓就以為萬事大吉了,這種想法是萬萬要不得的.

可能一開始學(xué)博客會(huì)覺得很困難,語句組織不行,沒有什么要寫的,這時(shí)候你可以分析一下當(dāng)時(shí)你對(duì)這個(gè)問題或者Bug的理解,你是怎么想的,怎么思考的,怎么解決的,然后有什么感悟,這些都是可以寫上去.完全可以用大白話來說這些問題,我敢保證,你這樣寫十篇博客之后,你就知道你該如何組織你的語言了~

有人會(huì)問你比比這么多,那么寫博客到底有什么好處?其實(shí)我在以前的博客中有寫到過,這里我再總結(jié)一下,主要有以下的三點(diǎn)好處.

  • 博客是日常開發(fā)的筆記,一個(gè)程序猿是不能記太多的代碼的,這從我們成為程序猿的第一天就知道,所以我們要做的就是知道這個(gè)問題如何解決,當(dāng)我們發(fā)現(xiàn)了一個(gè)新的問題或者Bug,可能第一次花了我們很長時(shí)間才解決這個(gè)問題,如果我們不做記錄的話,那么下一次我們雖然知道思路,但是依然需要做很長時(shí)間的修正,可能比第一次提高20%-30%的效率,但是當(dāng)我們寫博客做筆記呢?下次遇到這樣的問題我們就會(huì)立馬知道我們的博客中對(duì)應(yīng)的解決方案,然后我們可以快速的找到解決方案,這樣工作效率最少提升50%-60%,這就是小時(shí)候我們常說的"好記性不如爛筆頭"的道理~~~~~

  • 如果出去面試,如何看出一個(gè)人的能力?這里有兩個(gè)程序猿,水平差不多,一個(gè)有著寫博客習(xí)慣,另外一個(gè)不寫博客習(xí)慣,我相信很多人會(huì)選擇前者,博客雖然不能概括出你所有的能力,但是最少能從其中窺其一二,所以,堅(jiān)持寫博客是很有必要的.

  • 寫博客還有個(gè)好處就是幫助別的程序猿,當(dāng)你發(fā)表了一篇關(guān)于某個(gè)問題解決方案的博客,可能更多的程序猿因此而解決問題,這是一個(gè)很好的過程,因?yàn)槲业牟┛蛶椭芏嗳?雖然這是無償?shù)?但是我依然非常高興.再就是可能有人可能對(duì)你的博客有著不同的理解,他們可能提出一些更好的方案或者解決辦法.通過這樣的方式你可了解更多更優(yōu)的解決方案,助人即助己...


結(jié)語


這篇博客寫了好幾天,算的上是技術(shù)雜談吧,如果有任何問題,歡迎隨時(shí)在下面評(píng)論,謝謝~


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末狠持,一起剝皮案震驚了整個(gè)濱河市疟位,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌喘垂,老刑警劉巖甜刻,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異正勒,居然都是意外死亡得院,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門昭齐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尿招,“玉大人,你說我怎么就攤上這事阱驾【兔眨” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵里覆,是天一觀的道長丧荐。 經(jīng)常有香客問我,道長喧枷,這世上最難降的妖魔是什么虹统? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任弓坞,我火速辦了婚禮,結(jié)果婚禮上车荔,老公的妹妹穿的比我還像新娘渡冻。我一直安慰自己,他們只是感情好忧便,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布族吻。 她就那樣靜靜地躺著,像睡著了一般珠增。 火紅的嫁衣襯著肌膚如雪超歌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天蒂教,我揣著相機(jī)與錄音巍举,去河邊找鬼。 笑死凝垛,一個(gè)胖子當(dāng)著我的面吹牛懊悯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苔严,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼定枷,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼孤澎!你這毒婦竟也來了届氢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤覆旭,失蹤者是張志新(化名)和其女友劉穎退子,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體型将,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寂祥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了七兜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丸凭。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖腕铸,靈堂內(nèi)的尸體忽然破棺而出惜犀,到底是詐尸還是另有隱情,我是刑警寧澤狠裹,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布虽界,位于F島的核電站,受9級(jí)特大地震影響涛菠,放射性物質(zhì)發(fā)生泄漏莉御。R本人自食惡果不足惜撇吞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望礁叔。 院中可真熱鬧牍颈,春花似錦、人聲如沸琅关。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽死姚。三九已至人乓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間都毒,已是汗流浹背色罚。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留账劲,地道東北人戳护。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像瀑焦,于是被迫代替她去往敵國和親腌且。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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

  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5榛瓮? 答:HTML5是最新的HTML標(biāo)準(zhǔn)铺董。 注意:講述HT...
    kismetajun閱讀 27,482評(píng)論 1 45
  • 初次見面請(qǐng)多關(guān)照
    qiao老胖閱讀 97評(píng)論 0 0
  • 1.打開PS,導(dǎo)入自己的圖片 這個(gè)是自己摸索出來比較費(fèi)時(shí)的方法禀晓,但是自己不是專門玩PS的精续,所以將就著用吧。 2.找...
    2017CBwithMe閱讀 4,659評(píng)論 0 0
  • 天賦仙姿粹懒,玉骨冰肌重付。向炎威,獨(dú)逞芳菲凫乖。輕盈雅淡确垫,初出香閨。是水宮仙帽芽,月宮子删掀,漢宮妃。清夸苫卜嚣镜,韻勝酴糜爬迟。笑江梅,雪...
    許川山閱讀 278評(píng)論 0 0
  • FLAT【教學(xué)游戲】系列專欄 打造快樂有效的外語課堂 圣誕節(jié)又快要到了菊匿,各位FLATers應(yīng)該又開始緊鑼密鼓籌備各...
    FLAT_edu閱讀 473評(píng)論 0 0