
簡介
回想起來,從畢業(yè)到現(xiàn)在在iOS這個行業(yè)也努(hua)力(shui)了好幾年,每每看到同事加班到深夜,于心不忍,故寫這篇博客,總結(jié)自己這幾年寫代碼的感悟,希望能幫助到那些加班到深夜的程序猿們.這篇博客主要有兩個主題,一是代碼規(guī)范,而是提升效率.雖然兩者看似風(fēng)牛馬不相及,但其中的聯(lián)系可是大大的存在,當(dāng)你注重了代碼規(guī)范,那么你的代碼質(zhì)量對應(yīng)的提升,反正,最終你加班的次數(shù)減少就對了~
文章也是隨意寫的,沒有什么順序,也就想到哪寫到哪,各位大佬就當(dāng)做飯后茶資來看吧~
正確的理解什么叫做寫代碼,理解業(yè)務(wù)邏輯的重要性
對于什么叫寫代碼,什么叫程序猿?首先來談?wù)勎易约簩懘a的觀點,開發(fā)一個項目其中 50%-70%的工作量為理解業(yè)務(wù)邏輯,剩余的部分為編寫代碼,而在編寫代碼部分70%的工作量為處理異常情況,只有30%的工作量是開發(fā)程序.所以理解項目的業(yè)務(wù)邏輯是非常有必要的,因為業(yè)務(wù)邏輯決定UI和UE.例如,某個新增數(shù)據(jù)的Button 當(dāng)用戶沒有權(quán)限的時候,是不允許點擊的,當(dāng)你不管這些,允許沒有權(quán)限的用戶點擊操作該Button,那么就很有可能出現(xiàn)Bug,這是最常見的例子.所以,對于項目中業(yè)務(wù)邏輯,雖然不用做到倒背如流,但是最少要做到熟讀于心.
合理架構(gòu)代碼,提高工作效率
在寫代碼之前,一定要先去架構(gòu)自己的代碼結(jié)構(gòu),讓它盡量變的合理起來,靈活合理的代碼結(jié)構(gòu)會讓你更高效的工作,切忌先實現(xiàn),后優(yōu)化的理念去架構(gòu)代碼,有些程序猿(這幾年遇到不少)就是喜歡使用先實現(xiàn),后優(yōu)化的理念去架構(gòu)代碼,或者是連優(yōu)化都沒有,想到一種實現(xiàn)方式就立馬開始碼代碼,結(jié)果一堆Bug存在了自己寫的代碼里面.反正各種隱患,日積月累,Bug越來越多.到最后自己都不想去處理了,而且很多時候還是拆了東墻補西墻的情況,反正種種情況不斷. 下面我就分享一下我寫代碼的兩種架構(gòu)方式.
正所謂業(yè)務(wù)邏輯決定代碼邏輯,所以我們可以通過業(yè)務(wù)邏輯來架構(gòu)我們的代碼結(jié)構(gòu).例如,現(xiàn)在業(yè)務(wù)邏輯中的帖子列表,只有展示和新增的邏輯,你就要立馬去想會不會在后面的版本有刪除或者修改的功能,或者還有分享的功能呢?是否需要給這些功能預(yù)留接口或者位置?用戶會不會有其他想法或者操作?每一種業(yè)務(wù)情景都可能對應(yīng)著Bug,架構(gòu)代碼之前多考慮業(yè)務(wù)邏輯是很有必要的.
當(dāng)你需要修改某個代碼模塊的時候,這時候你也要先去思考當(dāng)你修改這個業(yè)務(wù)邏輯會不會對其他模塊造成影響,這里主要可以通過耦合性來去聯(lián)想其他模塊,然后去思考如何架構(gòu)代碼才能讓兼容性更好.這樣修改代碼是否會對后面的代碼迭代造成影響?
當(dāng)然了上面的只是簡單的舉例而已,有自己認(rèn)為合理的架構(gòu)方式歡迎評論.....??
合理復(fù)用代碼,業(yè)務(wù)邏輯代碼盡量復(fù)用,UI邏輯代碼少復(fù)用.
復(fù)用代碼,在很大程度上可以減少代碼的重復(fù)率,一個重復(fù)率很高的代碼工程不是一個合格的工程,所以,復(fù)用代碼是非常有必要的.
但是我們一定要去合理的復(fù)用代碼,不合理的復(fù)用代碼會造成的最常見問題就是代碼臃腫,耦合度高.例如,我們一個ViewController視圖控制器在UI的展現(xiàn)形式上在每一個地方都是一致的,但是每一個地方都需要不同的邏輯,有的是只展示,有的是既展示有可以跳轉(zhuǎn),有的是只跳轉(zhuǎn)不展示種種邏輯.如果我們都復(fù)用這個視圖控制器的話,那么這個視圖控制器的邏輯代碼會非常的多,各個使用這個控制器的模塊也會因此變得耦合度高了起來.
那么我們應(yīng)該遵循一個怎樣的復(fù)用規(guī)律呢?那就是業(yè)務(wù)邏輯代碼盡量復(fù)用,UI邏輯代碼少復(fù)用(PS:安卓的布局文件盡量復(fù)用,不涉及邏輯代碼,盡量復(fù)用).為什么這么說呢?這是因為業(yè)務(wù)邏輯決定著UI的展現(xiàn),業(yè)務(wù)邏輯發(fā)生改變,UI一般就發(fā)生了改變,相反,只要業(yè)務(wù)邏輯不發(fā)生改變,業(yè)務(wù)邏輯代碼也不需要發(fā)生改變.所以,業(yè)務(wù)邏輯代碼盡量多復(fù)用,例如網(wǎng)絡(luò)請求方法,我們寫在一個統(tǒng)一的文件中,誰用誰調(diào)用即可.只有當(dāng)后臺發(fā)生變化的時候,我們才需要修改代碼,大大的提高效率.
合理理解'閉環(huán)'現(xiàn)象,任何入口代碼在用戶使用過程中都需要出口代碼.
這里我稱之為'閉環(huán)'現(xiàn)象,也就是說任何入口代碼都需要出口代碼.當(dāng)然了,這是我的個人感覺,與其說這是代碼習(xí)慣,不如說它是我的一種思考習(xí)慣.而且我常常通過這種形式來完善我的代碼,比如我Push一個界面,我就會想到底有多少種方式Pop到上一個界面?每一種方式會不會有其他的分支情況等等,再例如用戶進(jìn)入了某個狀態(tài),怎么樣才能回到初始狀態(tài)?需不需要回到初始狀態(tài)(當(dāng)然,在想這種問題都是假設(shè)能回到初始狀態(tài),完成一個'閉環(huán)'現(xiàn)象.)等等, 還有就是下面寫到的if 和switch 的完整性問題,我也是常常用到這種思考方式,來驗證我的代碼是否完整,一個不是'閉環(huán)'的代碼多多少少都會有點Bug.太深層次的我還沒有體驗,比如一個對象的創(chuàng)建必然會有對應(yīng)的銷毀過程,等等.
利用百度和Google解決日常問題和Bug.

程序猿日常開發(fā)過程中不免遇到這樣或者那樣的問題或者Bug,那么正確解決問題的姿勢是什么呢?
一般情況下,我會分下面幾步步驟操作.
- 一、回想自己以前是否遇到過類似問題或者Bug,自己的博客是否有記錄過這種問題(博客是程序猿很好的解決問題途徑).有沒有聽說過類似的問題.
- 二、回想發(fā)現(xiàn)沒有該類似問題,那就思考問題可能出現(xiàn)的原因,仔細(xì)檢查自己的代碼邏輯,尋找問題可能出現(xiàn)的位置,打斷點驗證正確性.
- 三、還是沒有發(fā)現(xiàn)問題,這時候,我們就要百度或者Google了,我們要把具體的問題盡量提取出關(guān)鍵字來查詢,提高查詢效率.比如,日志的錯誤碼或者錯誤信息等等,都是關(guān)鍵信息.
- 四、其實上面的三步就已經(jīng)差不多把問題給解決了,但是還是有一些很具體的問題,怎么辦?我們要去回想我們身邊的大佬有沒有談及這塊的內(nèi)容,如果有,我們?nèi)ピ儐?盡量去詢問解決思路,而不是解決方法.比如,當(dāng)時我學(xué)習(xí)Java的時候,我就問當(dāng)時我們老大,我說'老大,有沒有相關(guān)的書籍或者學(xué)習(xí)網(wǎng)站呢?',而不是去問'老大,你教教我Java吧' 爾爾之語.最后想別人請教的時候,最好是有償?shù)?比如發(fā)個紅包什么的,數(shù)量不用太大,這樣做有兩個原因,一,讓別人知道你愿意為知識付費,這樣別人以后更喜歡幫助你.二,提醒自己,都TM是錢吶,別隨便去請教別人問題,自己動手,豐衣足食.....
說一下反面教材,我曾經(jīng)碰到不止一個人問我問題,"你好,大佬,我這里有個問題,我把代碼發(fā)你,你給我看看吧",''大佬,可不可給我解決這個問題?(其實連文章都沒看,就讓我解決)"如此爾爾,還有很多的人覺得在工作中向別人提問問題是一種好學(xué)的體現(xiàn),但是我要說的是醒醒吧,你已經(jīng)不在學(xué)生時代了,醒醒吧你的老師已經(jīng)不在你身邊了,你去向別人提問問題,讓別人給你解決,就有可能是浪費他的工作時間,來幫助你,那他的工作可能就完成不了,被老板罵是他,被老板噴是他.當(dāng)然了,對于騷棟自己而言,我還是很喜歡幫助別人的,只是不喜歡伸手黨而已.
善用 return 和 break 關(guān)鍵詞
return 和 break 代碼中常用的關(guān)鍵詞,其實還有一個關(guān)鍵詞continue,這里簡單的說明一下三者的作用以及不同之處.return是用來結(jié)束一個方法,break是來結(jié)束一個循環(huán)體,continue是來結(jié)束某個循環(huán)體中的一次循環(huán).
那么為什么要善于運用 return 和 break 呢? 這主要是當(dāng)數(shù)組遍歷的時候,我們已經(jīng)尋找到了我們想要的數(shù)據(jù)的時候,我們就可以停止循環(huán)體,或者停止函數(shù)了,具體是選擇return 還是break ,要根據(jù)獲取到我們想要的數(shù)據(jù)后續(xù)是否還有操作來作為依據(jù).下面我們就舉例來說明.
例: 返回數(shù)組中元素值為"test"的下標(biāo)(有且只有一個),并且組成"第x個元素為test"返回,沒有則返回nil
- 在未做優(yōu)化代碼之前, 我們一般會想到我們要在循環(huán)體的外部創(chuàng)建一個字符串空對象,然后遍歷數(shù)組,找到符合條件的下標(biāo),組裝字符串,然后在循環(huán)體外返回.但是這樣做就會可能造成性能的浪費,比如要是數(shù)組元素個數(shù)為10個,符合下標(biāo)的元素是在第一位,也就是說后面九次的循環(huán)都是毫無意義的,從而造成資源的浪費.
- (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個元素為test",i + 1];
}
}
return thirdItem;
}
- 下面為優(yōu)化過后的代碼, 我們直接把return放在了if當(dāng)中,這樣當(dāng)在數(shù)組中找到合適的元素的時候就會立馬跳出函數(shù).不會有過多的性能浪費,我們要把握的時機就是只要當(dāng)函數(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個元素為test",i + 1];
}
}
return nil;
}
break關(guān)鍵詞和上面的基本一致,主要是用于在當(dāng)前函數(shù)當(dāng)中跳出循環(huán)體時還需要做其他操作.這里就不多細(xì)說了.看例子吧~
//返回數(shù)組中元素值為"test"的下標(biāo)(有且只有一個),并且組成"內(nèi)容為test的元素的下一個下標(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的元素的下一個下標(biāo)為%d",index);
}
- 代碼優(yōu)化之后
//返回數(shù)組中元素值為"test"的第一個下標(biāo),并且組成"第x個元素為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的元素的下一個下標(biāo)為%d",index);
}
關(guān)于 if 和 switch 產(chǎn)生 Bug 的思考
我可說在很多的初級小白百分之五十的Bug都是由于情況考慮不全導(dǎo)致的,那么在體現(xiàn)在代碼上是什么樣呢?在代碼上主要就是if和switch寫的不完整造成情況考慮不全從而產(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
對比上面的兩種if,很多看官又會說到,臥槽,你這是侮辱我智商呢?我剛剛學(xué)習(xí)編程就會了,只是后面為了方便,所以就不寫完整了,其實我工作以來也是基本上很少寫完整的if語句,能少些就少些,代碼同時整潔易懂.何樂而不為?但是要注意的是,在代碼層面上你可以不寫完整,但是你在心中一定要去把if語句的所有情況進(jìn)行窮舉,因為每一個if分支情況都可能是一個隱藏的Bug,這可能是業(yè)務(wù)邏輯方面的,也可能是代碼邏輯方面的.所以對if語句進(jìn)行窮舉操作是很有必要的.
那么對于switch是一樣的情況,switch中有default關(guān)鍵詞,很多時候,我們并不寫default部分,但是default部分也算是一個情況分支,這是我們所需要注意.但是有一種情況例外,那么就是switch的判斷條件為枚舉值的時候,這時候,情況總體個數(shù)已經(jīng)根據(jù)枚舉值的多少而定下了,所以不需要寫default部分了.
列表視圖能局部刷新絕對不全部刷新.
對于列表刷新是我們?nèi)粘i_發(fā)中最常見的一個操作,例如數(shù)據(jù)的刪除,新增,變動,修改等等都需要我們?nèi)ニ⑿铝斜?很多時候我們都是直接使用[self.mainTableView reloadData];來刷新數(shù)據(jù),但是我們仔細(xì)想想假定就只有一個或者有限的Cell需要刷新,你使用上面的那句話,那不是白白造成了許多的內(nèi)存資源浪費嗎?所以我們能使用局部刷新絕對不使用全部刷新.
舉例子說明,iOS這邊我們能使用下面方法就使用下面方法進(jìn)行局部刷新,雖然在代碼量會有所提升,但是不會造成大量的資源浪費.
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
善用宏定義和枚舉,減少魔法數(shù)字的使用.
何為魔法數(shù)字?就是根本沒有任何的解釋,隨心所欲的寫在代碼之中的數(shù)字,反正就是讓人不明覺厲的那種就對了~ 魔法數(shù)字的危害性主要會體現(xiàn)在項目后期的維護(hù)上,在開發(fā)階段的時候,你根據(jù)隨手寫上了一個魔法數(shù)字,可能是寬高信息,可能是邊距信息,但是你沒有寫任何的注釋來表明這個數(shù)字是怎么來的,是做什么用的,我相信不出三個月,連你去看這個你當(dāng)前的魔法數(shù)字都會覺得很神秘.所以,在你的代碼中減少魔法數(shù)字扥出現(xiàn)是很有必要的.
那么如何去消除魔法數(shù)字這種危害性呢?
一,增加合理注釋,解釋這個魔法數(shù)字是如何產(chǎn)生的,在代碼當(dāng)中有著怎么樣的作用,雖然這樣可以在一定程度上解決了魔法數(shù)字的危害,注釋卻多的一皮,還有就是后期維護(hù)非常麻煩,假設(shè)很多的魔法數(shù)字分布在你的項目各個角落中,后期你要改的話,需要先去找到這個魔法數(shù)字的位置,然后再去修改,是你自己寫的代碼還好,如果是別人的代碼,光這個找的時間,就夠自己喝一壺的了~
二,既然使用注釋的方式不能完全解決魔法數(shù)字問題,我們就看一下使用宏定義和枚舉如何解決魔法數(shù)字問題.(其實當(dāng)某一種魔法數(shù)字少量的時候,使用注釋是完全可行的~,酌情而定)
- 宏定義方式來定義魔法數(shù)字,全局都可能用到的魔法數(shù)字,我們就放在pch文件中,如果只是某幾個類可能用到的文件,我們就直接創(chuàng)建一個.h文件,然后需要的導(dǎo)入即可,對于單個類使用的魔法數(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),我們一個布爾值就可以解決了,如果是多種狀態(tài),如果不用枚舉的話,到時候代碼中各種
if (style == 1) {}等等魔法數(shù)字,完全讓人摸不到頭腦,各種翻文檔,各種翻接口找到對應(yīng)的業(yè)務(wù)意義.大大浪費了時間.但是我們?nèi)绻嗣杜e類型了呢? 我們就可以快速的通過字面的意思推測出類型的意義,例如if (style == DrawStyleLine) {},我們可以清楚的明白我們的繪制的樣式為線性,定義的枚舉類型如下所示.
typedef enum : NSUInteger {
DrawStyleLine,
DrawStyleSquare,
DrawStyleCircle,
DrawStyleArrow,
DrawStyleHand,
} DrawStyle;
當(dāng)然,宏定義和枚舉除了能解決魔法數(shù)字問題,還能解決書寫錯誤問題,比如我們因為不小心把if (style == 1) {}寫成if (style == 10) {}在編譯過程中是沒有任何錯誤的,只有在運行過程中才可能暴露出其對應(yīng)的Bug來,但是我們?nèi)绻褂煤甓x或者枚舉,我們書寫不全,在編譯過程中就直接顯示錯誤,例如把DrawStyleLine寫成DrawStyleLina,編譯器會直接提示我們書寫錯誤,這樣也會有助于避免我們在這些小問題上翻車.
多利用 位移枚舉 的位運算實現(xiàn)業(yè)務(wù)邏輯中多選操作
我們經(jīng)常會在iOS中的.h看到這樣的枚舉,例如對于貝塞爾曲線的指定角進(jìn)行切邊操作,用到的枚舉類型,如下所示.
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 << 0,
UIRectCornerTopRight = 1 << 1,
UIRectCornerBottomLeft = 1 << 2,
UIRectCornerBottomRight = 1 << 3,
UIRectCornerAllCorners = ~0UL
};
這時候我們會發(fā)現(xiàn)枚舉類型的值并不是我們常見的0,1,2,3等等,而是1 << 0,1 << 2,1 << 3等等,如下圖所示,這代表著位運行的表示形式, 示例解釋如下所示.
1 << 0 代表著 十進(jìn)制的 1 左移 0 位 那么就是 0001 (十進(jìn)制為1,具體運算為1(2^0)),
1 << 1 代表著 十進(jìn)制的 1 左移 1 位 那么就是 0010 (十進(jìn)制為2,具體運算為1(2^1)),
1 << 2 代表著 十進(jìn)制的 1 左移 2 位 那么就是 0100(十進(jìn)制為4,具體運算為1*(2^2)),

再來給各位小白惡補一下位運算的幾種運算符號的意義
位運算的幾種常用運算符號的意義
- << 左移運算符,就是將某一個整數(shù)的二進(jìn)制整體左移n位,例如 整數(shù)5(二進(jìn)制表示為0101)的位運算 5 << 1,那么結(jié)果就是整數(shù)10 (二進(jìn)制為1010);
- >> 右移運算符,就是將某一個整數(shù)的二進(jìn)制整體右移n位,和左移運算符類似.
- & 按位與運算符,只有對應(yīng)的兩個二進(jìn)位均為1時,結(jié)果位才為1,否則為0, 例如5&9=1,解釋為0101&1001=0001,轉(zhuǎn)化成整數(shù)就是1.
- | 按位或運算符,只要對應(yīng)的二個二進(jìn)位有一個為1時,結(jié)果位就為1,否則為0, 例如5|9=13,解釋為0101|1001=1101,轉(zhuǎn)化成整數(shù)就是13.
那么說了這么多,位移枚舉的位運算到底有什么的用途呢?其實,這樣的枚舉任意幾個枚舉值相加的值(用其 按位或運算即可~) 都是不一樣的,不信可以試驗一下~我們也就是說可以對枚舉值的任意組合進(jìn)行判斷,我們就用UIRectCorner來說明一下,假設(shè)我們當(dāng)我們選擇的是UIRectCornerTopLeft和UIRectCornerTopRight的時候,我們就讓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];
}
有人就會問我們用普通的枚舉來做多選會有什么問題,下面我來定義一個枚舉類型,大家來看一下,仍然用UIRectCorner來做說明.
// 錯誤演示
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 ,
UIRectCornerTopRight = 2,
UIRectCornerBottomLeft = 3,
UIRectCornerBottomRight = 4,
UIRectCornerAllCorners = 5
};
當(dāng)我們選擇 UIRectCornerTopLeft|UIRectCornerTopRight的時候計算出來的值為3,也就是說選擇 UIRectCornerTopLeft|UIRectCornerTopRight和選擇UIRectCornerBottomLeft是沒有任何區(qū)別的.因為我們的判斷依據(jù)只能是枚舉所代表的值.這樣就會出現(xiàn)了問題,做不成多選操作,這種類型的枚舉只能來做單選操作.
當(dāng)然了,還是會有人比比用下面的例子說,這樣不是也能多選嗎?但是 1 就是 1 << 0, 2就是 1 << 1,其他的都是等同的,這里就不多比比了~
typedef NS_OPTIONS(NSUInteger, UIRectCorner) {
UIRectCornerTopLeft = 1 ,
UIRectCornerTopRight = 2,
UIRectCornerBottomLeft = 4,
UIRectCornerBottomRight = 8,
UIRectCornerAllCorners = ~0UL
};
合理理解高內(nèi)聚,低耦合 控制單個文件的代碼量
在上一家公司的時候,那時候的我還是那么天真單純,當(dāng)我接手iOS項目時,再一次刷新了我的三觀,這是為什么呢?因為這個項目被上一伙人解耦解到支離破碎的,邏輯分散的各個角落中了,簡直是慘不忍睹.最后一問原來是有后臺開發(fā)大佬參與了開發(fā)~ 后來我接觸了Java后臺,我才明白為什么會寫的支離破碎,在Java的前后端不分離的webApp中,View和Controller就是完全分離的~但是在iOS中,View和Controller的邏輯在一定程度上是內(nèi)聚的.當(dāng)然了,埋怨當(dāng)時的后臺開發(fā)人員,畢竟每一種編程語言都有一定的規(guī)則.
好了,言歸正傳,我們來說說高內(nèi)聚,低耦合的問題,高內(nèi)聚,低耦合這個概念我相信在學(xué)編程之初,你的老師就一定提過,高內(nèi)聚就是讓我們要把相關(guān)度比較高的部分盡可能的集中,不要分散.但是一旦過分高內(nèi)聚,就會造成代碼臃腫不堪,業(yè)務(wù)邏輯混亂復(fù)雜的情況,而低耦合就是讓我們把兩個相關(guān)的模塊盡可以能把依賴的部分降低到最小,不要讓兩個系統(tǒng)產(chǎn)生強依賴.但是如果過度低耦合,那么就會造成上面的那種情況,代碼邏輯支離破碎,代碼可讀性非常差.所以具體的高內(nèi)聚,低耦合的概念如何在你的代碼中體現(xiàn),是需要一定的編程經(jīng)驗的~ 當(dāng)然,高內(nèi)聚低耦合這個概念的標(biāo)準(zhǔn),什么時候該內(nèi)聚,什么該解耦,在每一個程序猿眼里,我相信都是不一樣,有的人認(rèn)為這個部分應(yīng)該解耦,認(rèn)為邏輯堆在這里過于臃腫,但是有的人卻認(rèn)為這里的代碼根據(jù)業(yè)務(wù)邏輯就應(yīng)該堆在這里,可以提高代碼的可讀性,所以這個標(biāo)準(zhǔn)是只可意會不可言傳的,哈哈.只要心中有這個概念,不用刻意去追求,水到渠成即可~
通過內(nèi)聚和解耦,我們可以合理的控制單個代碼文件的代碼量,其實我不建議一個代碼文件中的代碼量太多.這樣會造成代碼非常的臃腫,可讀性也是很差的.比如我以前寫過一個列表的九宮格Cell(每一種情況都是一個新的UI),里面的代碼超過兩千多行,著實是臃腫不堪~維護(hù)起來非常的麻煩.這時候,我們就可以把部分的代碼抽出來,寫在一個新的文件中.當(dāng)然了,如果實在是解耦不了,我們一定要去加注釋,注明這里的代碼是干什么用的,為什么要這么做等等,為后期維護(hù)或者二次開發(fā)做好鋪墊工作.
合適使用注釋 ,相對于“做了什么”,更應(yīng)該說明“為什么這么做”
代碼千萬行,注釋第一行;編程不規(guī)范,同事兩行淚
通過上面的詩句,我們就可以深刻的體會到注釋的重要性,從我們開始寫第一行的代碼時候,很多的大佬就會教導(dǎo)我們一定要把注釋寫好,寫明白,我想大多數(shù)程序猿會有這種感覺,如果不寫注釋,當(dāng)我們自己會看自己三個月前的代碼時,我們都會大聲的說一句,"我擦,這是寫的什么鬼~ ,這肯定不是我寫的",所以寫注釋,寫好注釋,這是一種利人利己的行為,何樂為不為?網(wǎng)上很多有很多寫注釋的重要性的博客或者文章,我想都是深受其害的程序猿~
在寫注釋的時候,既不能像大姨媽一樣拖拖拉拉的寫一堆,也不能為了成為衛(wèi)生標(biāo)兵就一點也不做注釋,合理的注釋會讓你的代碼可讀性更高(其實,就算你把注釋注的再詳細(xì),我想別人也不愿意去看你寫的代碼,通病而已,??),我們在寫注釋的時候,不但要寫明這代碼是干什么用的,有時候更應(yīng)該寫為什么要去這么寫,你當(dāng)時所想的想法是什么,比如有個計算Cell的高度的時候,由于里面可能有固定高度也可能有可變高度,我一般會在Cell的.h文件中如下圖進(jìn)行類似注釋(雖然這個類是用來做tableViewHeaderView的,但是是一樣的.).詳細(xì)的注明,Cell的高度是怎么來的,哪怕是日后再也看這些代碼也是很輕松的.要不然,真的就成了"編程不規(guī)范,同事兩行淚"了.

利用 狀態(tài)機 和 枚舉 完成一個控制器多種狀態(tài)UI展示效果
狀態(tài)機的這個概念我第一次接觸是在Untiy 3D 做游戲用到的,其實就是一個監(jiān)控狀態(tài)流轉(zhuǎn)的模塊,例如,一個人有三種操作,一個是停止吃喝,一個是吃飯,一個是喝水.我們可以讓一個人從吃飯到停止吃喝,或者從停止吃喝到吃飯,但是我們不能讓一個人吃飯直接切換到喝水.因為我們要停止了吃飯,再去喝水,當(dāng)然了,你非要一手吃飯一手喝水也行~ 請出門右轉(zhuǎn)不送.狀態(tài)的流轉(zhuǎn),什么樣的狀態(tài)可以流轉(zhuǎn)到什么狀態(tài),不可以流轉(zhuǎn)到什么樣的狀態(tài),我們做一總結(jié),這就是初步的狀態(tài)機.
當(dāng)我們在一個頁面中我們有多種UI要展示,而我們要放在同一個控制器中,那么我們就要考慮狀態(tài)的流轉(zhuǎn)了,其實也就是狀態(tài)機的問題.如果不做好狀態(tài)的流轉(zhuǎn),可能會導(dǎo)致邏輯代碼分散到各個位置,反正就是一個字,亂.
這時候,我們要先去理清UI的狀態(tài)是如何流轉(zhuǎn)的,然后我們要定義枚舉,有多少種狀態(tài)就定義多少種枚舉值.例如我做過的一個關(guān)于Socket的項目中就有一個界面根據(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做,我不多說,自行體會去.部分代碼(已經(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這個方法就可以得到我們所需要的UI了.其實,這個模塊算的上是一個開發(fā)經(jīng)驗吧,如果有這種需要可以用上這種模式,這樣寫我個人感覺把UI狀態(tài)流轉(zhuǎn)放到一個地方更方便去管理,在代碼的可讀性上也會有更大的提高.
合理使用懶加載.尤其是在 removeFromSuperView時候,就是兩字,真香.
按需加載,是優(yōu)化代碼的一個重要的途徑.先說說我自己吧,寫了這么多年,一直在使用懶加載的形式創(chuàng)建控件,其實在那些視圖要出現(xiàn)的就需要加載完成的控件身上,懶加載并沒有提高什么效率,反而讓代碼量上升了,這種情形最好的好處也就是代碼規(guī)范整潔,不用所以的控件初始化都擠在一個方法里面,其他別無用途.但是當(dāng)我們有彈窗這種用戶主觀調(diào)出的視圖的時候,我們就可以用懶加載的形式,這樣當(dāng)用戶需要的時候,我們才去分配內(nèi)存,初始化控件,不用在父類初始過程中就要分配內(nèi)存空間,降低了程序內(nèi)存峰值,提高了效率.
但是我要說的時候,什么時候使用懶加載是最爽的?那就是當(dāng)Cell中有個控件,有的數(shù)據(jù)需要展示,有的數(shù)據(jù)則不需要展示,我們在配合上下劃線直接訪問屬性的形式,就是兩字,真香.
說的再多,也可能是白扯,我們看一下例子~ 這里有一個班級選擇列表,當(dāng)用戶選擇某個班級的時候,后面才會會出現(xiàn)選中按鈕,否則不會出現(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;
}
但是在賦值的時候就是提高性能的時候,我們的想法是Cell上是否含有圖片控件,我們都刪除.然后重新添加圖片控件,這就是我們?yōu)槭裁丛谶@里使用的是_selectImageView的原因,_selectImageView可以不通過set方法直接訪問成員屬性,所以假設(shè)圖片控件沒有被創(chuàng)建,那么就是[nil removeFromSuperview]了,為什么不用[self.selectImageView removeFromSuperview];呢?因為一旦self.selectImageView就會調(diào)用get方法從而創(chuàng)建控件,懶加載也就失去了意義.通俗講法就是,刪除控件的時候,控件沒有初始化我就不進(jìn)行刪除,如果有,那么我就進(jìn)行刪除控件操作.從而提高代碼效率.
//賦值數(shù)據(jù)時
- (void)setDataModel:(ClassModel *)dataModel {
_dataModel = dataModel;
[_selectImageView removeFromSuperview];
if (dataModel.isSelect) {
[self.contentView addSubview:self.selectImageView];
}
}
及時解決代碼冗余問題
代碼冗余這種問題說大不大,說小不小,代碼冗余每一個工程都或多或少有這樣的問題,其實冗余的代碼在業(yè)務(wù)邏輯上并不會有太大的影響,但是在后期代碼維護(hù)上是存在著一定的問題,一定程度上增加了閱讀難度,所以當(dāng)你發(fā)現(xiàn)自己的代碼冗余的時候,一定要及時刪除冗余的代碼.
學(xué)習(xí)并使用優(yōu)秀的三方組件,嘗試自己封裝一些侵入性低的組件
首先說明一點,雖然以前的我造過不少的輪子,我不太提倡在工作中重復(fù)的去造輪子,這主要是因為自己造的輪子可能由于開發(fā)時間過短會存在各種問題或者Bug.而且還浪費你的工作時間,降低了工作效率.很多的優(yōu)秀的三方迭代的較多,所以穩(wěn)定性很好,如果你在工作中需要某一個三方,我建議先去網(wǎng)上找找看,如果有合適的,盡量使用那么穩(wěn)定的三方來解決工作中的問題.提高自己的工作效率,這樣就不用天天加班到深夜了.
很多優(yōu)秀的三方組件都是值得我們?nèi)W(xué)習(xí)的,我們可以通過查看組件源碼的形式學(xué)習(xí)開發(fā)者當(dāng)時的開發(fā)邏輯,看多了,自然也就懂了.
當(dāng)然了,在我們業(yè)余的時間,我們可以去嘗試封裝一些入侵性較低的組件,下面有侵入性的解釋,侵入性就伴隨著耦合問題,所以在封裝組件的時候,組件的侵入性是一個很好的衡量組件優(yōu)劣的方式.
當(dāng)你的代碼引入了一個組件,導(dǎo)致其它代碼或者設(shè)計,要做相應(yīng)的更改以適應(yīng)新組件.這樣的情況我們就認(rèn)為這個新組件具有侵入性.
做好釋放工作,完成"閉環(huán)"現(xiàn)象
當(dāng)一塊內(nèi)存被分配的時候,你就需要想這塊內(nèi)存需不需要你自己來釋放它(也就是上面提到的"閉環(huán)"現(xiàn)象).當(dāng)你確定這塊內(nèi)存需要你來釋放,不妨提前寫好釋放代碼,防止自己遺忘.大大減少因為內(nèi)存釋放問題所造成的Bug或者問題的數(shù)量.反正牢記"創(chuàng)建就要銷毀"的理念就行了.
不炫技,簡單易懂才是最屌的代碼
自己學(xué)到了某一個新的框架或者組件,總是想著把它使用到我們的項目當(dāng)中,雖然這樣是沒有問題的,但是我們忽略了它的穩(wěn)定性和可讀性,一個新的框架可能會存在很多的問題或者Bug,所以代碼的穩(wěn)定性是一個很大的問題,再加上當(dāng)別人來接手你的代碼時,很有可能因為這些新的框架而需要額外的學(xué)習(xí)時間,從而造成了工作效率的降低,這都是一些潛在的風(fēng)險.
保持函數(shù)的功能單一性,控制每個函數(shù)的代碼量
我們在構(gòu)建一個函數(shù)之前,我們要思考這個函數(shù)到底在我們程序中扮演著什么樣的功能模塊,從而保持函數(shù)的功能單一性,從代碼結(jié)構(gòu)上來說,一個功能單一的函數(shù)更利于閱讀,同時,由于我們需要保持每個函數(shù)的功能單一性就必然會去抽離代碼,重新組裝新的函數(shù),這樣每一個函數(shù)的代碼量都不會有太多.閱讀起來相當(dāng)?shù)妮p松.
例如,我寫的自定義UITableViewCell都是通過懶加載的方式抽離出代碼,如下圖所示,這樣的代碼層次感就出現(xiàn)了,使人更容易理解代碼邏輯.而不是把所有的控件初始化都放在init方法中,如果這樣做的話,雖然沒有任何的問題,但是到底是什么控件添加了什么控件就需要仔細(xì)閱讀代碼了,增加了閱讀的難度.

淺談NSTimer的釋放問題
很多iOS初級開發(fā)者在使用NSTimer做定時器功能的時候,往往一個不消息就會造成了內(nèi)存泄露問題,當(dāng)然了,這也包括我在內(nèi),我們來舉例說明一下NSTimer的釋放問題.
首先,我們來舉例子說明一下NSTimer的循環(huán)引用.首先我們在ViewController分類中定義一個NSTimer的成員變量.如下所示.
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSTimer *timer;
@end
然后我們在下面的delloc中釋放該NSTimer對象.
@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
然后我們就會發(fā)現(xiàn)dealloc方法根本不走,也就是說我們釋放不了這個NSTimer對象,這樣就循環(huán)引用了 ,然后有人就說,你用strong強引用NSTimer對象了.所以釋放不了,但是我要告訴就算我改成下面哪種方式,循環(huán)引用依然是存在的.
@interface ViewController ()
@property(nonatomic,weak)NSTimer *timer;
@end
@interface ViewController ()
{
NSTimer *timer;
}
@end
那么NSTimer循環(huán)問題到底出現(xiàn)在哪里呢?這個循環(huán)引用的根源是在Target上,其實NSTimer的target參數(shù)會被RunLoop所持有(此時ViewController對象引用計數(shù)為2),也就是說銷毀界面的時候,ViewController對象引用計數(shù)依然是1,故不能被釋放,也就不能走dealloc方法.所以造成了循環(huán)引用.

既然知道了問題所在,我們只要打破環(huán)中一個位置即可,這里常見的方式就是在用戶主動調(diào)用的方法中釋放NSTimer,先釋放NSTimer,然后RunLoop釋放了對ViewController對象的持有,ViewController對象的引用計數(shù)變?yōu)?,然后銷毀界面ViewController對象的引用計數(shù)變成0,對象被成功銷毀,如下所示.
//假設(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)了一個新的NSTimer構(gòu)建方法,那就是使用block的形式,雖然能解決上的問題,但是依然需要注意block中self的循環(huán)引用問題,具體方法如下圖所示.

總結(jié)NSTimer釋放秘訣就是下面的這句話.
通過用戶主動操作調(diào)用的方法中來釋放NSTimer,任何時候都不要在dealloc中釋放NSTimer.
不建議在if中添加過長的判斷語句,如果需要,那么就分行顯示,提高代碼的可讀性
if分支語句中的判斷條件有時候很多,如下所示.如果我們順著寫,代碼不但臃腫了~ 閱讀起來及其的不方便.這時候我們就可以使用分行顯示的形式,來展示我們的判斷條件.提高閱讀效率.如下所示.
// 未優(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è)某一個條件過長的時候,我們也可以利用抽離的方式,讓代碼看起來整潔大方.具體例子如下所示.
// 未優(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)格,一個良好的代碼風(fēng)格是一個程序猿最基本的要求,一個良好的代碼風(fēng)格可以讓別人在閱讀自己寫的代碼時候更加輕松.萬萬不可在每一個代碼文件中都有著不同的風(fēng)格,這樣在閱讀代碼的時候肯定是非常難受的.寫代碼的時候我們要做到自己現(xiàn)在水平的最好,要像對待自己的孩子一樣對待自己的代碼,你只有呵護(hù)它,它更好的才能回報于你.下面我們就談一下幾種常見的代碼規(guī)范.可以稍微參考.
參考于<<如何提高代碼的可讀性? - 讀《編寫可讀代碼的藝術(shù)》>>
-
命名規(guī)范問題
①
命名盡量使用駝峰命名法,命名的時候不可隨意取名,例如 action1 ,盡量要做到見名知意.
②
我們知道駝峰命名可以很清晰地體現(xiàn)變量的含義,但是當(dāng)駝峰命名中的單元超過了3個之后,就會很影響閱讀體驗:
userFriendsInfoModel
memoryCacheCalculateTool
是不是看上去很吃力?因為我們大腦同時可以記住的信息非常有限,尤其是在看代碼的時候,這種短期記憶的局限性是無法讓我們同時記住或者瞬間理解幾個具有3~4個單元的變量名的。所以我們需要在變量名里面去除一些不必要的單元.(PS:這一點我還真沒做到....??)
③
不能使用大家不熟悉的縮寫
有些縮寫是大家熟知的:
doc 可以代替document
str 可以代替string
但是如果你想用BEManager來代替BackEndManager就比較不合適了。因為不了解的人幾乎是無法猜到這個名稱的意義的。
所以類似這種情況不能偷懶,該是什么就是什么,否則會起到相反的效果。因為它看起來非常陌生,跟我們熟知的一些縮寫規(guī)則相去甚遠(yuǎn)。
-
提高代碼的美觀性
①
在聲明一組變量的時候,由于每個變量名的長度不同,導(dǎo)致了在變量名左側(cè)對齊的情況下,等號以及右側(cè)的內(nèi)容沒有對齊:
NSString *name = userInfo[@"name"];
NSString *sex = userInfo[@"sex"];
NSString *address = userInfo[@"address"];
而如果使用了列對齊的方法,讓等號以及右側(cè)的部分對齊的方式會使代碼看上去更加整潔:
NSString *name = userInfo[@"name"];
NSString *sex = userInfo[@"sex"];
NSString *address = userInfo[@"address"];
這二者的區(qū)別在條目數(shù)比較多以及變量名稱長度相差較大的時候會更加明顯。
②
當(dāng)涉及到相同變量(屬性)組合的存取都存在的時候,最好以一個有意義的順序來排列它們:
- 讓變量的順序與對應(yīng)的HTML表單中<input>字段的順序相匹配
- 從最重要到最不重要排序
- 按照字母排序
舉個例子:相同集合里的元素同時出現(xiàn)的時候最好保證每個元素出現(xiàn)順序是一致的。除了便于閱讀這個好處以外,也有助于能發(fā)現(xiàn)漏掉的部分,尤其當(dāng)元素很多的時候:
//給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;
③
有些時候,你的某些代碼風(fēng)格可能與大眾比較容易接受的風(fēng)格不太一樣。但是如果你在你自己所寫的代碼各處能夠保持你這種獨有的風(fēng)格,也是可以對代碼的可讀性有積極的幫助的。
比如一個比較經(jīng)典的代碼風(fēng)格問題:
if(condition){
}
or:
if(condition)
{
}
對于上面的兩種寫法,每個人對條件判斷右側(cè)的大括號的位置會有不同的看法。但是無論你堅持的是哪一個,請在你的代碼里做到始終如一。因為如果有某幾個特例的話,是非常影響代碼的閱讀體驗的。
熟悉常用的顏色以及顏色的表示方式

作為一個程序猿,日常開發(fā)中在UI方面說的最多的可能就是"RGB值","#f5f5f5","RGBA"等等,每一天都和各種各樣的顏色打交道,但是我見過很多的程序猿對顏色這塊的知識實在太少了,今天,我們就簡單地聊聊關(guān)于顏色一些常識.
而我們在日常中UI給我們最多的就是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)建一個簡單的顏色,比如我只想要紅色,那么我們就讓紅色的值不為0,然后綠色和藍(lán)色的值都是0即可,比如#FF0000,就是最滿的紅色,當(dāng)紅色的值變小時,顏色逐漸趨于黑色,我們可以通過下面來來了解這種變化.

再例如,我們可以通過本模塊的第一個圖來調(diào)出黃色,黃色就是紅色加上綠色,那么RGB十六進(jìn)制表示方式就為#FFFF00.其他的顏色以此類似.
我們接下來說一個比較有意思的顏色,那就是灰色,很多專業(yè)屬于稱之為中性灰,灰色是怎么來的呢?灰色其實就是RGB三個值是一樣的即可. 例如 #C0C0C0 , #F5F5F5等等,只有是#ABABAB或者#AAAAAA的形式都是中性灰.這里還要說一個中性灰叫做 #808080 ,很多人稱之為絕對中性灰.
寫博客是一個快速提高的姿勢
從我剛開始工作開始,我就一直在寫博客,雖然也是中間也是斷斷續(xù)續(xù),但是我絕對要說寫博客是讓一個程序猿快速成長的良好途徑.當(dāng)然了,我說的寫博客并不是讓你去抄網(wǎng)上的博客,一頓CV完了之后就沒事了,寫博客主要是寫自己的學(xué)習(xí)代碼和記錄問題,而不是去抄襲,就算是抄襲同一個問題,你也要加上自己的觀點,談?wù)勀銓@個問題的理解.而不是抄一頓就以為萬事大吉了,這種想法是萬萬要不得的.
可能一開始學(xué)博客會覺得很困難,語句組織不行,沒有什么要寫的,這時候你可以分析一下當(dāng)時你對這個問題或者Bug的理解,你是怎么想的,怎么思考的,怎么解決的,然后有什么感悟,這些都是可以寫上去.完全可以用大白話來說這些問題,我敢保證,你這樣寫十篇博客之后,你就知道你該如何組織你的語言了~
有人會問你比比這么多,那么寫博客到底有什么好處?其實我在以前的博客中有寫到過,這里我再總結(jié)一下,主要有以下的三點好處.
博客是日常開發(fā)的筆記,一個程序猿是不能記太多的代碼的,這從我們成為程序猿的第一天就知道,所以我們要做的就是知道這個問題如何解決,當(dāng)我們發(fā)現(xiàn)了一個新的問題或者Bug,可能第一次花了我們很長時間才解決這個問題,如果我們不做記錄的話,那么下一次我們雖然知道思路,但是依然需要做很長時間的修正,可能比第一次提高20%-30%的效率,但是當(dāng)我們寫博客做筆記呢?下次遇到這樣的問題我們就會立馬知道我們的博客中對應(yīng)的解決方案,然后我們可以快速的找到解決方案,這樣工作效率最少提升50%-60%,這就是小時候我們常說的"好記性不如爛筆頭"的道理~~~~~
如果出去面試,如何看出一個人的能力?這里有兩個程序猿,水平差不多,一個有著寫博客習(xí)慣,另外一個不寫博客習(xí)慣,我相信很多人會選擇前者,博客雖然不能概括出你所有的能力,但是最少能從其中窺其一二,所以,堅持寫博客是很有必要的.
寫博客還有個好處就是幫助別的程序猿,當(dāng)你發(fā)表了一篇關(guān)于某個問題解決方案的博客,可能更多的程序猿因此而解決問題,這是一個很好的過程,因為我的博客幫助很多人,雖然這是無償?shù)?但是我依然非常高興.再就是可能有人可能對你的博客有著不同的理解,他們可能提出一些更好的方案或者解決辦法.通過這樣的方式你可了解更多更優(yōu)的解決方案,助人即助己...

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