為什么要有編碼規(guī)范這么一說(shuō)呢?你設(shè)想一下如果張三寫(xiě)代碼一個(gè)風(fēng)格,李四一個(gè)風(fēng)格,當(dāng)張三去看李四的時(shí)候他會(huì)極度的不舒服,反過(guò)來(lái)李四去看張三的代碼的時(shí)候也會(huì)感覺(jué)非常的糟糕。所以他們兩個(gè)人遵循同一個(gè)規(guī)則的時(shí)候,再去看對(duì)方的代碼的時(shí)候就不會(huì)不適應(yīng),因此指定一個(gè)大家都遵循的代碼編程風(fēng)格就至關(guān)重要了。對(duì)于iOS開(kāi)發(fā),蘋(píng)果官方就制定了一套編碼規(guī)范。作為iOS開(kāi)發(fā)者遵循蘋(píng)果制定的編碼規(guī)范寫(xiě)代碼,那么在看蘋(píng)果的官方代碼的時(shí)候就覺(jué)得很輕松了。
命名
這點(diǎn)非常重要?。?!沒(méi)有重復(fù)三遍,但是依然是最重要的?。?!如果你不是一開(kāi)始就跟著項(xiàng)目走而是在中途或者是需要你維護(hù)一個(gè)別人留下的項(xiàng)目的時(shí)候。如果項(xiàng)目是經(jīng)過(guò)幾手,而且沒(méi)有統(tǒng)一的命名風(fēng)格,而且還沒(méi)有注釋。這時(shí)候你面對(duì)項(xiàng)目,你的內(nèi)心是崩潰的。如果可以把寫(xiě)這些代碼的揪出來(lái),我保證你會(huì)把他打的不像人樣!好的命名勝過(guò)N多的注釋?zhuān)?/p>
命名的一般性原則
清晰性
- 要清晰且簡(jiǎn)短,但是要注意不要因?yàn)楹?jiǎn)短放棄簡(jiǎn)短,清晰是首位。
- 名稱(chēng)通常不縮寫(xiě),即使名稱(chēng)很長(zhǎng)也要拼寫(xiě)完全。(你有可能比較討厭蘋(píng)果那長(zhǎng)長(zhǎng)的方法名,但是你看到方法就知道是干嘛的,連文檔都省了,因此OC也有自文檔語(yǔ)言的美譽(yù))。
- 避免有歧義的命名。歧義是一個(gè)造成誤會(huì)的罪魁禍?zhǔn)住?/li>
一致性
代碼命名的一致性能帶給閱讀代碼者非常的閱讀體驗(yàn),能讓人一眼就看出這大概是什么。
- 盡可能使用與Cocoa編程接口命名保持一致的名稱(chēng)。如果你不太確定某個(gè)命名的一致性,請(qǐng)瀏覽一下頭文件或參考文檔中的范例。
- 在使用多態(tài)方法的類(lèi)中,命名的一致性非常重要。在不同類(lèi)中實(shí)現(xiàn)相同功能的方法應(yīng)該具有相同的名稱(chēng)。
不要自我指涉
自我指涉的英文是:self-reference,如果你不明白什么是自我指涉,給你講個(gè)故事吧。從前有座山,山上有座廟,廟里有個(gè)和尚,和尚說(shuō),從前有座山……
- 首先名稱(chēng)不要自我指涉,看下面的代碼。
NSString //okey
NSStringObject //自我指涉
- 掩碼(可使用位操作進(jìn)行組合)和用作通知名稱(chēng)的常量不受該約定限制。
前綴
前綴不僅可以防止和蘋(píng)果的命名發(fā)生沖突,而且也可以區(qū)分軟件的功能范圍,比如UIKit里面的都以UI開(kāi)頭,看到UI開(kāi)頭的類(lèi)我們就能知道是UI相關(guān)的。
- 前綴有規(guī)定的格式。它由兩到三個(gè)大寫(xiě)字符組成,不能使用下劃線與子前綴。
- 命名class,protocol,structure,函數(shù),常量時(shí)使用前綴;命名成員方法時(shí)不使用前綴,因?yàn)榉椒ㄒ呀?jīng)在它所在類(lèi)的命名空間種;同理,命名結(jié)構(gòu)體字段時(shí)也不使用前綴。
書(shū)寫(xiě)約定
書(shū)寫(xiě)約定能規(guī)范命名,不然你會(huì)看到其他語(yǔ)言的影子,比如以前寫(xiě)C++的你就會(huì)發(fā)現(xiàn)他會(huì)帶一股濃濃的C++味道。OC也應(yīng)該有OC的味道。來(lái)看看OC對(duì)于書(shū)寫(xiě)有哪些約定吧!
- 對(duì)于包含多個(gè)單詞的名稱(chēng),不要使用標(biāo)點(diǎn)符號(hào)作為名稱(chēng)的一部分或作為分隔符(下劃線,破折號(hào)等)。大寫(xiě)每個(gè)單詞的首字符并將這些單詞連續(xù)拼寫(xiě)在一起。并且要注意一下限制:
- 方法名小寫(xiě)第一個(gè)單詞的首字符,大寫(xiě)后續(xù)所有單詞的首字符。方法名不使用前綴。
- 函數(shù)名和常量名使用與其關(guān)聯(lián)類(lèi)相同的前綴,并且要大寫(xiě)前綴后面所有單詞的首字符。
- 避免使用下劃線來(lái)表示名稱(chēng)的私有屬性。蘋(píng)果公司保留該方式的使用。如果第三方這樣使用可能 會(huì)導(dǎo)致命名沖突,他們可能會(huì)在無(wú)意中用自己的方法覆蓋掉已有的私有方法,這會(huì)導(dǎo)致嚴(yán)重的后 果。(蘋(píng)果是不建議用私有變量的,這一點(diǎn)在用runtime解決一些問(wèn)題的時(shí)候常常用到,因此得注意。在命名的時(shí)候不要把其他語(yǔ)言的習(xí)慣帶到OC中)?。
類(lèi)與協(xié)議命名要注意的事兒
- 類(lèi)名應(yīng)包含一個(gè)明確??述該類(lèi)(或類(lèi)的對(duì)象)是什么或做什么的名詞。類(lèi)名要有合適的前綴。
- 協(xié)議應(yīng)該根據(jù)對(duì)方法的行為分組方式來(lái)命名。
關(guān)于頭文件
- 聲明孤立的類(lèi)或協(xié)議:將孤立的類(lèi)或協(xié)議聲明放置在單獨(dú)的頭文件中,該頭文件名稱(chēng)與類(lèi)或協(xié)議同名。
- 聲明相關(guān)聯(lián)的類(lèi)或協(xié)議:將相關(guān)聯(lián)的聲明(類(lèi),類(lèi)別及協(xié)議) 放置在一個(gè)頭文件中,該頭文件名稱(chēng)與 主要的類(lèi)/類(lèi)別/協(xié)議的名字相同。
- 包含框架頭文件:每個(gè)框架應(yīng)該包含一個(gè)與框架同名的頭文件,該頭文件包含該框架所有公開(kāi)的頭文 件。(寫(xiě)一個(gè)庫(kù)的時(shí)候一般得這么干,你去看看蘋(píng)果的庫(kù)或者出名的苦,里面有一個(gè)和庫(kù)同名的頭文件,里面全是其他頭文件的引用)。
- 為已有框架中的某個(gè)類(lèi)擴(kuò)展 API:如果要在一個(gè)框架中聲明屬于另一個(gè)框架某個(gè)類(lèi)的范疇類(lèi)的方法, 該頭文件的命名形式為:原類(lèi)名+“Additions”。
- 相關(guān)聯(lián)的函數(shù)與數(shù)據(jù)類(lèi)型:將相聯(lián)的函數(shù),常量,結(jié)構(gòu)體以及其他數(shù)據(jù)類(lèi)型放置到一個(gè)頭文件中,并
以合適的名字命名。
關(guān)于方法命名
除了上述要注意的,還有一些小的問(wèn)題也需要注意。
- 表示對(duì)象行為的方法,名稱(chēng)以動(dòng)詞開(kāi)頭。
- 名稱(chēng)中不要出現(xiàn) do 或 does,因?yàn)檫@些助動(dòng)詞沒(méi)什么實(shí)際意義。也不要在動(dòng)詞前使用副詞或形容詞修飾。
- 如果方法返回方法接收者的某個(gè)屬性,直接用屬性名稱(chēng)命名。不要使用 get,除非是間接返回一個(gè)或 多個(gè)值。
- 參數(shù)要用??述該參數(shù)的關(guān)鍵字命名。
- 參數(shù)前面的單詞要能??述該參數(shù)。
- 不要使用and來(lái)連接用屬性作參數(shù)關(guān)鍵字。一般不用and,除非滿足下面這條。
- 如果方法??述兩種獨(dú)立的行為,使用and來(lái)串接它們。
關(guān)于訪問(wèn)方法
訪問(wèn)方法就是我們常說(shuō)的setter和getter方法。那么在寫(xiě)(或重寫(xiě))訪問(wèn)方法的時(shí)候要注意些什么呢?
- 如果屬性是用名詞??述的,則命名格式為:
- (void) setNoun:(type)aNoun;
- (type) noun;
- 如果屬性是用形容詞??述的,則命名格式為:
- (void) setAdjective:(BOOL)flag;
- (BOOL) isAdjective;
- 如果屬性是用動(dòng)詞??述的,則命名格式為:(動(dòng)詞要用現(xiàn)在時(shí)時(shí)態(tài))
- (void) setVerbObject:(BOOL)flag;
- (BOOL) verbObject;
- 不要使用動(dòng)詞的過(guò)去分詞形式作形容詞使用.
- 可以使用情態(tài)動(dòng)詞(can,should,will等)來(lái)??高清晰性,但不要使用do或does.
- 只有在方法需要間接返回多個(gè)值的情況下,才使用get.
關(guān)于委托方法
在開(kāi)發(fā)中會(huì)經(jīng)常用到代理設(shè)計(jì)模式,但是代理方法也是有一定的命名規(guī)則的。委托方法的這些規(guī)則也使用于
- 名稱(chēng)以標(biāo)示發(fā)送消息的對(duì)象的類(lèi)名開(kāi)頭,省略類(lèi)名的前綴并小寫(xiě)類(lèi)第一個(gè)字符。經(jīng)常用的tabview的代理方法或者數(shù)據(jù)源方法都是tabview開(kāi)頭的。
- 冒號(hào)緊跟在類(lèi)名之后(隨后的那個(gè)參數(shù)表示委派的對(duì)象)。該規(guī)則不適用于只有一個(gè) sender 參數(shù)的 方法。
- 面的那條規(guī)則也不適用于響應(yīng)通知的方法。在這種情況下,方法的唯一參數(shù)表示通知對(duì)象。
- (void) windowDidChangeScreen:(NSNotification *)notification;
- 用于通知委托對(duì)象操作即將發(fā)生或已經(jīng)發(fā)生的方法名中要使用did或will.
- 用于詢問(wèn)委托對(duì)象可否執(zhí)行某操作的方法名中可使用did或will,但最好使用should.
關(guān)于集合方法
集合方法命名有如下一些限制和約定
- 如果集合中的元素?zé)o序,返回 NSSet,而不是 NSArray。
- 如果將元素插入指定位置的功能很重要,則需具備如下方法:
- (void) insertElement:(elementType)anObj atIndex:(int)index;
- (void) removeElementAtIndex:(int)index;
集合方法的實(shí)現(xiàn)要考慮如下細(xì)節(jié):
- 集合類(lèi)方法通常負(fù)責(zé)管理元素的所有者關(guān)系,在 add 或 insert 的實(shí)現(xiàn)代碼里會(huì) retain 元素,在 remove 的實(shí)現(xiàn)代碼中會(huì) release 元素.
- 當(dāng)被插入的對(duì)象需要持有指向集合對(duì)象的指針時(shí),通常使用 set... 來(lái)命名其設(shè)置該指針的方法,且不 要 retain 集合對(duì)象。
關(guān)于方法的參數(shù)
- 第一條是最基本的,參數(shù)小寫(xiě)第一個(gè)單詞首字母,大寫(xiě)后面單詞的首字母。
- 不要在參數(shù)名中使用 pointer 或 ptr,讓參數(shù)的類(lèi)型來(lái)說(shuō)明它是指針。
- 避免使用 one, two,...,作為參數(shù)名。這樣的參數(shù)都不好意思用,鬼知道這代表什么。
- 盡量不要縮寫(xiě),多寫(xiě)幾個(gè)字母又不會(huì)死人。
關(guān)于私有方法
- 不要使用下劃線表示自己私有方法名稱(chēng)的前綴,Apple保留了這種做法。
- 若要繼承 Cocoa framework 中一個(gè)超大的類(lèi)(如:NSView),并且想要使你的私有方法名稱(chēng)與基類(lèi) 中的區(qū)別開(kāi)來(lái),你可以為你的私有方法名稱(chēng)添加你自己的前綴。這個(gè)前綴應(yīng)該具有唯一性,如基于你 公司的名稱(chēng),或工程的名稱(chēng),并以“XX_”形式給出。比如你的工程名為"Byte Flogger",那么就可以是 “BF_addObject:”。這一點(diǎn)在我的博文分析別人的開(kāi)源庫(kù)的時(shí)候就發(fā)現(xiàn)就有人這么做。
關(guān)于函數(shù)的命名
在OC中是可以使用C函數(shù)的,所以這里的函數(shù)指的是C函數(shù)。
- 函數(shù)命名與方法命名相似,但有兩點(diǎn)不同:
- 它們有前綴,其前綴與你使用的類(lèi)和常量的前綴相同
- 大寫(xiě)前綴后緊跟的第一個(gè)單詞首字符
- 大多數(shù)函數(shù)名稱(chēng)以動(dòng)詞開(kāi)頭,這個(gè)動(dòng)詞??述該函數(shù)的行為。
實(shí)例變量與數(shù)據(jù)類(lèi)型的命名
實(shí)例變量
在為類(lèi)添加實(shí)例變量的時(shí)候需要注意以下幾點(diǎn):
- 避免創(chuàng)建public變量
- 使用@private,@protected顯式限定實(shí)例變量的訪問(wèn)權(quán)限
- 確保實(shí)例變量名簡(jiǎn)明扼要地??述了它所代表的屬性
- 如果實(shí)例變量被設(shè)計(jì)為可被訪問(wèn)的,確保編寫(xiě)了訪問(wèn)方法
枚舉常量
- 使用枚舉來(lái)定義一組相關(guān)的整數(shù)常量
- 枚舉常量與其typedef遵守函數(shù)命名規(guī)則
- 位掩碼常量可以使用不具名枚舉
其他常量
- 通常不使用 #define來(lái)創(chuàng)建常量。如上面所述,整數(shù)常量請(qǐng)使用枚舉,浮點(diǎn)數(shù)常量請(qǐng)使用const
- 使用大寫(xiě)字母來(lái)定義預(yù)處理編譯宏。如:#ifdef DEBUG
- 編譯器定義的宏名首尾都有雙下劃線。如:MACH
- 為 notification 名及 dictionary key 定義字符串常量,從而能夠利用編譯器的拼寫(xiě)檢查,減少書(shū)寫(xiě)錯(cuò)誤
通知
- 如果一個(gè)類(lèi)有委托,那它的大部分通知可能由其委托的委托方法來(lái)處理。這些通知的名稱(chēng)應(yīng)該能夠反應(yīng)其 響應(yīng)的委托方法。比如,當(dāng)應(yīng)用程序??交 NSApplicationDidBecomeActiveNotification 通知時(shí),全局 NSApplication 對(duì)象的委托會(huì)注冊(cè)從而能夠接收 applicaitonDidBecomeActive: 消息。
通知由具有如下形式的全局 NSString 對(duì)象標(biāo)識(shí):
[相關(guān)聯(lián)類(lèi)的名稱(chēng)] + [Did 或 Will] + [UniquePartOfName] + Notification
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
開(kāi)發(fā)中的一些小貼士
在initialize類(lèi)方法中,能夠編寫(xiě)實(shí)現(xiàn)一些延遲執(zhí)行且只被一次的代碼,initialize類(lèi)方法是由運(yùn)行時(shí)系統(tǒng)在該類(lèi)響應(yīng)任何其他消息之前調(diào)用的。典型的應(yīng)用是在其中設(shè)置類(lèi)的版本信息。運(yùn)行時(shí)系統(tǒng)向每個(gè)類(lèi)發(fā)送initialize消息,即使該類(lèi)沒(méi)有實(shí)現(xiàn)initialize,也會(huì)調(diào)用其基類(lèi)的某個(gè)initialize方法。因此一個(gè)類(lèi)的initialize方法可能會(huì)因?yàn)榇嬖诶^承類(lèi)的緣故被執(zhí)行多次。因此有必要使用一定的技巧來(lái)防止只執(zhí)行一次的代碼被多次執(zhí)行。如:NSFoo類(lèi)的initialize方法實(shí)現(xiàn)可能如下:
+ (id) initialize{
if (self == [NSFoo class]){
//初始化代碼
}
return self;
}
要注意不應(yīng)當(dāng)顯式調(diào)用initialize方法。如果你需要激活initialize方法,使用[NSFoo self]形式的調(diào)用。
小結(jié)
習(xí)慣是平時(shí)一點(diǎn)點(diǎn)養(yǎng)成的,只要堅(jiān)持就會(huì)有好的習(xí)慣。