類催跪、方法及變量的命名是Objective-C編程的重要環(huán)節(jié)守屉。新手通常會(huì)覺(jué)得這門(mén)語(yǔ)言很繁瑣惑艇,因?yàn)槠湔Z(yǔ)法結(jié)構(gòu)使得代碼讀起來(lái)和句子一樣。名稱中一般都帶有"in"拇泛、"for"滨巴、"with"等介詞,其他編程語(yǔ)言則很少使用這些它們認(rèn)為多余的字眼俺叭。以下面這段代碼為例:
NSString *text = @"The quick brown fox jumped over the lazy dog";
NSString *newText = [text stringByReplacingOccurrencesOfString:@"fox" withString:@"cat"];
此代碼用了比較啰嗦的方式來(lái)描述一個(gè)看上去如此簡(jiǎn)單的表達(dá)式恭取。對(duì)于執(zhí)行替換操作的那個(gè)方法,其名字居然有48個(gè)字符長(zhǎng)熄守。不過(guò)這樣做的好處是蜈垮,代碼讀起來(lái)像日常語(yǔ)言里的句子:
"將文本中出現(xiàn)的"fox"字符串替換為"cat"耗跛,并返回替換后的新字符串。"(Take text and give me a new string by replacing the occurrences of the string 'fox' with the string 'cat')
這個(gè)句子準(zhǔn)確描述了開(kāi)發(fā)者想做的事攒发。在命名不像Objective-C這般復(fù)雜的語(yǔ)言中调塌,類似的程序可能會(huì)寫(xiě)成這樣:
string text = "The quick brown fox jumped over the lazy dog";
string newText = text.replace("fox", "cat");
這樣寫(xiě)有個(gè)問(wèn)題,就是text.replace()的兩個(gè)參數(shù)到底按何種順序解讀惠猿。是"fox"為"cat"所替換羔砾,還是"cat"為"fox"所替換呢?還有個(gè)疑問(wèn):replace函數(shù)是把所有出現(xiàn)的字符串都替換掉呢,還是只替換掉第一次找見(jiàn)的那個(gè)偶妖?其名稱沒(méi)能清除地表達(dá)出這兩個(gè)意思姜凄。而Objective-C的命名方式雖然長(zhǎng)一點(diǎn),但是卻非常清晰趾访。
讀者也會(huì)注意到态秧,方法與變量名使用了"駝峰式大小寫(xiě)命名法"(camel casing)--以小寫(xiě)字母開(kāi)頭,其后每個(gè)單詞首字母大寫(xiě)扼鞋。類名也用駝峰命名法申鱼,不過(guò)其首字母要大寫(xiě),而且前面通常還有兩三個(gè)前綴字母(參見(jiàn)第15條)藏鹊。在編寫(xiě)Objective-C代碼時(shí)润讥,大家一般都使用這種命名方式。如果你愿意盘寡,也可使用自己的風(fēng)格來(lái)命名楚殿,不過(guò)按照駝峰命名法寫(xiě)出來(lái)的代碼更容易為其他Objective-C開(kāi)發(fā)者所接受。
方法命名
你要是寫(xiě)過(guò)C++或Java代碼的話竿痰,應(yīng)該會(huì)習(xí)慣那種較為簡(jiǎn)省的函數(shù)名脆粥,在那種命名方式下,若想知道每個(gè)參數(shù)的用途影涉,就得查看函數(shù)原型变隔。這會(huì)令代碼難于讀懂: 為了明白函數(shù)用法,你必須經(jīng)承非悖回過(guò)頭來(lái)參照其原型匣缘。比方說(shuō),要寫(xiě)一個(gè)表示矩形的類鲜棠。用C++代碼可以這樣定義此類:
class Rectangle {
public:
Rectangle(float width, float height);
float getWidth();
float getHeight();
private:
float width;
float height;
};
不熟悉C++也沒(méi)關(guān)系肌厨,你只要知道這個(gè)類包含名為width及height的兩個(gè)實(shí)例變量就好。若想創(chuàng)建該類的實(shí)例豁陆,只有一種辦法柑爸,就是以矩形尺寸為參數(shù),調(diào)用其"構(gòu)造器"(constructor)盒音,寬度與高度都有對(duì)應(yīng)的存取方法表鳍∠诙可以用下面這行代碼來(lái)創(chuàng)建該類的實(shí)例:
Rectangle *aRectangle = new Rectangle(5.0f, 10.0f);
回顧這行代碼時(shí),并不能一下子看出5.0f和10.0f表示什么譬圣。你可能覺(jué)得這兩個(gè)參數(shù)是矩形尺寸瓮恭,可是到底寬度在先還是高度在先呢?要想確定這一點(diǎn)胁镐,還得去查函數(shù)定義才行偎血。
Objective-C語(yǔ)言就不會(huì)有這個(gè)問(wèn)題了,因?yàn)槠浞椒梢云鸬酶L(zhǎng)一些盯漂。熟悉C++的人可能會(huì)像下面這樣把Rectangle改寫(xiě)為等價(jià)的Objective-C代碼:
#import <Foundation/Foundation.h>
@interface EOCRectangle : NSObject
@property (nonatomic, assign, readonly) float width;
@property (nonatomic, assign, readonly) float height;
- (id)initWithSize:(float)width :(float)height;
@end
寫(xiě)這個(gè)類的人顯然知道,在Objective-C語(yǔ)言中笨农,和C++構(gòu)造器等效的東西是init-系列方法就缆,于是將其命名為"initWithSize:"。這看上去很奇怪谒亦,你也許覺(jué)得語(yǔ)法有誤竭宰,第二個(gè)冒號(hào)前面怎么沒(méi)有字呢?實(shí)際上語(yǔ)法完全沒(méi)錯(cuò)份招,之所以會(huì)覺(jué)得寫(xiě)錯(cuò)了切揭,是因?yàn)樗透膶?xiě)前的C++構(gòu)造器有著想同的問(wèn)題: 使用此類的開(kāi)發(fā)者還是不清楚每個(gè)變量的含義:
EOCRectangle *aRectangle =
[[EOCRectangle alloc] initWithSize:5.0f :10.0f];
下面這種命名方式就要好很多:
- (id)initWithWidth:(float)width andHeight:(float)height;
這么寫(xiě)是很長(zhǎng),然而這次絕對(duì)不會(huì)混淆每個(gè)變量的含義了:
EOCRectangle *aRectangle =
[[EOCRectangle alloc] initWithWidth:5.0f andHeight:10.0f];
雖說(shuō)使用長(zhǎng)名字可令代碼更為易讀锁摔,但是Objective-C新手還是難于習(xí)慣這種詳盡的方法命名風(fēng)格廓旬。不要吝于使用長(zhǎng)方法名。把方法名起的稍微長(zhǎng)一點(diǎn)谐腰,可以保證其能準(zhǔn)確傳達(dá)出方法所執(zhí)行的任務(wù)孕豹。然而方法名也不能長(zhǎng)得太過(guò)分了,應(yīng)盡量言簡(jiǎn)意賅十气。
以EOCRectangle類為例励背。好的方法名應(yīng)該像這樣:
- (EOCRectangle*)unionRectangle:(EOCRectangle*)rectangle
- (float)area
而下面這種命名方式咋不好:
- (EOCRectangle*)union:(EOCRectangle*)rectangle // Unclear
- (float)calculateTheArea // Too verbose
清晰的方法名從左至右讀起來(lái)好似一段文章。并不是說(shuō)非得按照那些命名規(guī)則來(lái)給方法起名砸西,不過(guò)這樣做可以令代碼變得更好維護(hù)叶眉,而且也能使其他人更易讀懂。
NSString這類就展示了一套良好的命名習(xí)慣芹枷。下面列出幾個(gè)方法及其命名緣由:
- +string
工廠方法(factory method)衅疙,用于創(chuàng)建新的空字符串。方法名清晰地描述了返回值的類型杖狼。 - +stringWithString
工廠方法炼蛤,根據(jù)某字符串創(chuàng)建出與之內(nèi)容相同的新字符串。與創(chuàng)建空字符串所用的那個(gè)工廠方法一樣蝶涩,方法名的第一個(gè)單詞也指明了返回類型理朋。 - +localizedStringWithFormat:
工廠方法絮识,根據(jù)特定格式創(chuàng)建出新的"本地化字符串"(localized string)。返回值類型是方法名的第二個(gè)單詞(string)嗽上,因?yàn)槠淝懊孢€有個(gè)修飾語(yǔ)(localized)用來(lái)描述其邏輯含義次舌。此方法的返回值依然是"字符串"(string),只不過(guò)是一種經(jīng)過(guò)本地化處理的特殊字符串兽愤。 - -lowercaseString
把字符串中的大寫(xiě)字母都轉(zhuǎn)為小寫(xiě)彼念。該方法不會(huì)像修改接收此消息的字符串本身,而是要新創(chuàng)建一個(gè)字符串浅萧,此做法也符合方法名中應(yīng)該包含返回值類型這一規(guī)范逐沙,然而描述返回值類型的單詞(string)前面還有個(gè)定語(yǔ)(lowercase)。 - -intValue
將字符串解析為整數(shù)洼畅。由于返回值是int吩案,所以方法名以這個(gè)詞開(kāi)頭。通常情況下我們不會(huì)像這樣來(lái)簡(jiǎn)寫(xiě)返回值的類型帝簇,比如string不簡(jiǎn)寫(xiě)為str徘郭,然而由于"Integer"(整數(shù))一詞的簡(jiǎn)稱"int"本身就是返回值的類型名稱,所以此處這么做是合理的丧肴。為了把方法名湊足兩個(gè)詞残揉,所以加了后綴"Value"。只有一個(gè)詞的名字通常用來(lái)表示屬性芋浮。由于int不是字符串對(duì)象的屬性抱环,所以要加Value以限定其含義。 - -length
獲取字符串長(zhǎng)度(也就是其字符個(gè)數(shù))途样。這個(gè)方法只有一個(gè)詞江醇,因?yàn)閷?shí)際上length也是字符串的一個(gè)屬性。這個(gè)屬性可能不是由實(shí)例變量來(lái)實(shí)現(xiàn)的何暇,然而即便如此陶夜,它也依然是字符串中的屬性。此方法若是命名為stringLength就不好了裆站。string一詞多余条辟,因?yàn)樵摲椒ǖ慕邮照呖隙ㄊ莻€(gè)字符串。 - -lengthOfBytesUsingEncoding
若字符串是以給定的編碼格式(ASCII宏胯、UTF8羽嫡、UTF6
等)來(lái)編碼的,則返回其字節(jié)數(shù)組的長(zhǎng)度肩袍。此方法與length相似杭棵,所以其命名原因也和剛才說(shuō)的一樣。此外扫倡,該方法還需一個(gè)參數(shù)惜索。該參數(shù)緊跟著方法名中描述其類型的那個(gè)名詞(encoding)癌蓖。 - -getCharacters:range:
獲取字符串中給定范圍內(nèi)的字符箕昭。其他語(yǔ)言里的獲取方法也許會(huì)以get開(kāi)頭,但Objective-C中一般不這么做绍傲,然而此處例外饵撑,該方法用get作其前綴惠险。原因在于撩笆,調(diào)用此方法時(shí)捺球,要在其首個(gè)參數(shù)中傳入數(shù)組,而該方法所獲取的字符正是要放到這個(gè)數(shù)組里面夕冲。此方法的完整簽名為: - -(void)getCharacters:(unichar *)buffer range:(NSRange)aRange
首個(gè)參數(shù)buffer應(yīng)該指向一個(gè)足夠大的數(shù)組氮兵,以便容納所請(qǐng)求范圍內(nèi)的那些字符。此方法要通過(guò)其參數(shù)來(lái)返回(這種參數(shù)通常稱為"輸出參數(shù)'(out-parameter))歹鱼, 而不通過(guò)返回值來(lái)返回胆剧,從內(nèi)存管理的角度看,這樣做更好醉冤。所有內(nèi)存管理事宜均由方法調(diào)用者處理,而不是先在此方法中創(chuàng)建一個(gè)數(shù)組篙悯,然后再又調(diào)用者釋放蚁阳。第二個(gè)參數(shù)前有個(gè)描述其類型的名詞(range),如果還有其他參數(shù)鸽照,也應(yīng)該在方法名中提到其類型螺捐。有時(shí)參數(shù)名前面還會(huì)加介詞,例如矮燎,此方法可以命名為"getCharacters:inRange:"定血。當(dāng)需要特別強(qiáng)調(diào)眾參數(shù)中的某一個(gè)時(shí),通常會(huì)這樣命名诞外。 - -hasPrefix:
判斷本字符串是否以另一個(gè)字符串開(kāi)頭澜沟。由于返回值是Boolean類型,所以為了讀起來(lái)像個(gè)句子峡谊,這種方法的名稱中通常都包括has("是否有")一詞茫虽。例如:
[@"Effective Objective-C" hasPrefix:@"Effective"] == YES
要是把方法名直接寫(xiě)成"prefix",讀起來(lái)就不這么順了既们。反之濒析,若將其叫成"isPrefixWith:"則聽(tīng)上去冗長(zhǎng)而別扭。
- -isEqualToString:
判斷兩字符串是否相等啥纸。其返回值和"hasPrefix:"一樣号杏,都是Boolean型,為了便于述說(shuō)斯棒,方法名用is開(kāi)頭盾致。還有個(gè)地方也會(huì)用到is這個(gè)前綴詞主经,那就是Boolean型的屬性。比方說(shuō)绰上,有個(gè)屬性叫做enabled旨怠,則其兩個(gè)存取方法應(yīng)該分別起名為"setEnabled:"與isEnabled。
給方法命名時(shí)的注意事項(xiàng)可總結(jié)成下面幾條規(guī)則蜈块。
- 如果方法的返回值是新創(chuàng)建的鉴腻,那么方法名的首個(gè)詞應(yīng)是返回值的類型,除非前面還有修飾語(yǔ)百揭,例如localizedString爽哎。屬性的存取方法不遵循這種命名方式,因?yàn)橐话阏J(rèn)為這些方法不會(huì)創(chuàng)建新對(duì)象器一,即便有時(shí)返回內(nèi)部對(duì)象的一份拷貝课锌,我們也認(rèn)為那相當(dāng)于原有的對(duì)象。這些存取方法應(yīng)該按照其所對(duì)應(yīng)的屬性來(lái)命名祈秕。
- 應(yīng)該把表示參數(shù)類型的名詞放在參數(shù)前面渺贤。
- 如果方法要在當(dāng)前對(duì)象上執(zhí)行操作,那么就應(yīng)該包含動(dòng)詞请毛;若執(zhí)行操作時(shí)還需要參數(shù)志鞍,則應(yīng)該在動(dòng)詞后面加上一個(gè)或多個(gè)動(dòng)詞。
- 不要使用str這種簡(jiǎn)稱方仿,應(yīng)該用string這樣的全稱固棚。
- Boolean屬性應(yīng)加is前綴。如果某方法返回非屬性的Boolean值仙蚜,那么應(yīng)該根據(jù)其功能此洲,選用has或is當(dāng)前綴。
- 將get這個(gè)前綴留給那些借由"輸出參數(shù)"來(lái)保存返回值的方法委粉,比如說(shuō)呜师,把返回值填充到"C語(yǔ)言式數(shù)組"(C-style array)里的那種方法就可以使用這個(gè)詞做前綴。
類與協(xié)議的命名
應(yīng)該為類與協(xié)議的名稱加上前綴艳丛,以避免命名空間沖突(參見(jiàn)第15條)匣掸,而且應(yīng)該像給方法起名時(shí)那樣把詞句組織好,使其從左至右讀起來(lái)較為通順氮双。例如碰酝,在NSArray的子類中,有一個(gè)用于表示可變數(shù)組的類戴差,叫做NSMutableArray送爸,mutable這個(gè)詞放在array前面,用以表明這是一種特殊的array(數(shù)組)。
下面以iOS的UI庫(kù)UIKit為例袭厂,演示類與協(xié)議的命名慣例:
- UIView(類)
所有"視圖"(View)均繼承于此類墨吓。視圖是構(gòu)造用戶界面的基本單元,它們負(fù)責(zé)繪制按鈕纹磺、文本框(text field)帖烘、表格等控件。這個(gè)類的名字無(wú)須解釋即可自明題意(self-explanatory)秘症、開(kāi)頭的兩個(gè)字母"UI"是UIKit框架的通用前綴。 - UIViewController(類)
視圖類(UIView)負(fù)責(zé)繪制視圖乡摹,然而卻不負(fù)責(zé)指定視圖里面應(yīng)該顯示的內(nèi)容采转。這項(xiàng)工作由本類聪廉,也就是"視圖控制器"(view controller)來(lái)完成。其名稱從左至右讀起來(lái)很順故慈。 - UITableView(類)
這是一種特殊類型的視圖,可以顯示表格中的一系列條目察绷。所以,它在超類(UIView)名稱中的View一詞前面加了Table這個(gè)修飾詞克婶,用以和其他類型的視圖相區(qū)隔。在超類名稱前加修飾語(yǔ)是一種常用的命名慣例丹泉。本類也可以叫做UITableView情萤,不過(guò)這個(gè)名字無(wú)法完整傳達(dá)出"視圖"這個(gè)概念。開(kāi)發(fā)者必須查看接口聲明方能確定這一點(diǎn)筋岛。比方說(shuō)晒哄,想創(chuàng)建一個(gè)專門(mén)用來(lái)顯示圖像的表格視圖,那么就可以將這個(gè)繼承自UITableView的子類命名為EOCImageTableView寝凌。不過(guò)這時(shí)要加上自己的前綴EOC,而不是沿用超類的前綴UI(UIKit框架中的類以UI為前綴)红符。這么做的原因在于,你不應(yīng)該把自己的類放到其他框架的命名空間里面预侯,那些框架以后也許會(huì)新建同名的類。 - UITableViewController(類)
正如UITableView是一種特殊的view(視圖)一樣萎馅,UITableViewController也是一種特殊的view controller(視圖控制器),它專門(mén)用于控制表格視圖飒货。因此耍目,其命名方式與UITableView類似膏斤。 - UITableViewDelegate(協(xié)議)
此協(xié)議定義了表格視圖與其他對(duì)象之間的通信接口邪驮,命名時(shí),把定義"委托接口"(delegate interface)的那個(gè)類名(UITableView)放在前面沮榜,后面加上Delegate一詞喻粹,這樣讀起來(lái)順口。(第23條詳述了"委托模式"(Delegate pattern)守呜。)
說(shuō)了這么多,其中最重要的一點(diǎn)就是弥喉,命名方式應(yīng)該協(xié)調(diào)一致玛迄。而且,如果要從其他框架中繼承子類蓖议,那么務(wù)必遵循其命名慣例。比方說(shuō)勒虾,要從UIView類中繼承自定義的類,那么類名末尾的詞必須是View州弟。同理,若要?jiǎng)?chuàng)建自定義的委托協(xié)議婆翔,則其名稱中應(yīng)該包含委托發(fā)起方的名稱,后面再跟上Delegate一詞潭陪。如果能堅(jiān)持這種命名習(xí)慣最蕾,那么在稍后回顧自己的代碼或他人使用你所寫(xiě)的代碼時(shí),很容易就能理解其含義瘟则。
要點(diǎn)
- 起名時(shí)應(yīng)遵從標(biāo)準(zhǔn)的Objective-C命名規(guī)范,這樣創(chuàng)建出來(lái)的接口更容易為開(kāi)發(fā)者所理解慷嗜。
- 方法名要言簡(jiǎn)意賅丹壕,從左至右讀起來(lái)要像個(gè)日常用語(yǔ)中的句子才好。
- 方法名里不要使用縮略后的類型名稱菌赖。
- 給方法起名時(shí)的第一要?jiǎng)?wù)就是確保其風(fēng)格與你自己的代碼或所要集成的框架相符。