歡迎到我的 個(gè)人博客 http://liumh.com 瀏覽此文
本文談?wù)?iOS 開(kāi)發(fā)中的命名規(guī)范,主要涉及常量命名、枚舉命名皿曲、類及其方法命名,以及分類及其方法命名吴侦。如果你找的是官網(wǎng)的編碼規(guī)范屋休,請(qǐng)移步: Coding Guidelines for Cocoa。當(dāng)然本文會(huì)講一些官網(wǎng)沒(méi)有的東西备韧。
- 常量命名
- 用枚舉表示狀態(tài)劫樟、選項(xiàng)、狀態(tài)碼
- 類及其方法命名
- 分類及其方法命名
常量命名
iOS 開(kāi)發(fā)中,肯定避免不了要命名一些常量毅哗,那么听怕,我們應(yīng)該怎樣來(lái)命名常量呢捧挺?
在討論上述問(wèn)題前虑绵,先來(lái)了解定義常量的兩種方式。
第一種闽烙,使用 #define
預(yù)處理定義常量翅睛。例如:
#define ANIMATION_DURATION 0.3
定義一個(gè) ANIMATION_DURATION
常量來(lái)表示 UI 動(dòng)畫的一個(gè)常量時(shí)間,這樣黑竞,代碼中所有使用 ANIMATION_DURATION
的地方都會(huì)被替換成 0.3捕发,但是這樣定義的常量無(wú)類型信息,且如果你在調(diào)試時(shí)想要查看 ANIMATION_DURATION
的值卻無(wú)從下手很魂,因?yàn)樵陬A(yù)處理階段ANIMATION_DURATION
就已經(jīng)被替換了扎酷,不便于調(diào)試。因此遏匆,棄用這種方式定義常量法挨。
第二種,使用類型常量幅聘。將上面的預(yù)處理定義常量修改成類型常量:
static const NSTimeInterval kAnimationDuration = 0.3;
這樣就為常量帶入了類型信息凡纳,那么定義類型常量又有什么規(guī)范呢?
- 對(duì)于局限于某編譯單元(實(shí)現(xiàn)文件)的常量帝蒿,通常以字符
k
開(kāi)頭荐糜,例如上文中的kAnimationDuration
,且需要以static const
修飾葛超,例如:
static const NSTimeInterval kAnimationDuration = 0.3;
- 對(duì)于定義于類頭文件的常量暴氏,外部可見(jiàn),則通常以定義該常量所在類的類名開(kāi)頭绣张,例如
EOCViewClassAnimationDuration
, 仿照蘋果風(fēng)格偏序,在頭文件中進(jìn)行 extern 聲明,在實(shí)現(xiàn)文件中定義其值:
EOCViewClass.h
extern const NSTimeInterval EOCViewClassAnimationDuration;
EOCViewClass.m
const NSTimeInterval EOCViewClassAnimationDuration = 0.3;
對(duì)于字符串常量胖替,則會(huì)像這樣:
EOCViewClass.h
extern NSString *const EOCViewClassStringConstant;
EOCViewClass.m
NSString *const EOCViewClassStringConstant = @"EOCStringConstant";
常量定義是從右往左解讀研儒,上面的示例中就是定義了一個(gè)常量指針,其指向一個(gè) NSString 對(duì)象, 這樣該常量就不會(huì)被隨意修改独令。至于這種暴露出來(lái)的類常量最前面是否加上字母k
, 可以根據(jù)自己習(xí)慣在團(tuán)隊(duì)中進(jìn)行約定端朵,因?yàn)閺?iOS 的接口中我看到這兩種情況都有, 如
NSString *const UIApplicationLaunchOptionsRemoteNotificationKey;
NSString *const UIApplicationLaunchOptionsLocalNotificationKey;
還有:
NSString *const kCAAnimationCubic;
NSString *const kCAAnimationCubicPaced;
用枚舉表示狀態(tài)、選項(xiàng)燃箭、狀態(tài)碼
項(xiàng)目中冲呢,可用枚舉來(lái)表示一系列的狀態(tài)、選項(xiàng)和狀態(tài)碼招狸。例如 iOS SDK 中表示 UICollectionView 滑動(dòng)方向的枚舉定義如下:
typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) {
UICollectionViewScrollDirectionVertical,
UICollectionViewScrollDirectionHorizontal
};
或者定義 UITableView Style 的枚舉:
typedef NS_ENUM(NSInteger, UITableViewStyle) {
UITableViewStylePlain, // regular table view
UITableViewStyleGrouped // preferences style table view
};
從這里可以看出敬拓,定義的枚舉類型名稱應(yīng)以 2~3 個(gè)大寫字母開(kāi)頭邻薯,而這通常與項(xiàng)目設(shè)置的類文件前綴相同厕诡,跟隨其后的命名應(yīng)采用駝峰命名法則,命名應(yīng)準(zhǔn)確表述枚舉表示的意義营勤,枚舉中各個(gè)值都應(yīng)以定義的枚舉類型開(kāi)頭灵嫌,其后跟隨各個(gè)枚舉值對(duì)應(yīng)的狀態(tài)、選項(xiàng)或者狀態(tài)碼葛作。
對(duì)于需要以按位或操作來(lái)組合的枚舉都應(yīng)使用 NS_OPTIONS 宏來(lái)定義寿羞,例如 SDK 中:
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
這樣定義的選項(xiàng)能夠以 按位或操作符 來(lái)進(jìn)行組合,枚舉中每個(gè)值均可啟用或者禁用某一選項(xiàng)赂蠢,在使用時(shí)绪穆,可以使用 按位與操作符 來(lái)檢測(cè)是否啟用了某一選項(xiàng),如下:
UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
if (resizing & UIViewAutoresizingFlexibleWidth) {
// UIViewAutoresizingFlexibleWidth is set
}
另外虱岂,我們可能使用 switch 語(yǔ)句時(shí)玖院,會(huì)在最后加上 default 分支,但是若用枚舉定義狀態(tài)機(jī)量瓜,則最好不要使用 default 分支司恳,因?yàn)槿绻院笤偌恿艘环N狀態(tài),那么編譯器就會(huì)發(fā)出警告绍傲,提示新加入的狀態(tài)并未在 switch 分支中處理扔傅。假如寫上了 default 分支,那么它就會(huì)處理這個(gè)新?tīng)顟B(tài)烫饼,從而導(dǎo)致編譯器不發(fā)出警告猎塞,用 NS_ENUM 定義其他枚舉類型時(shí)也要注意此問(wèn)題。例如在定義代表 UI 元素樣式的枚舉時(shí)杠纵,通常要確保 switch 語(yǔ)句能正確處理所有樣式荠耽。
總結(jié)一下:
- 應(yīng)該用枚舉來(lái)表示狀態(tài)機(jī)的狀態(tài)、傳遞給方法的選項(xiàng)以及狀態(tài)碼等值比藻,給這些值起個(gè)易懂的名字铝量。
- 如果把傳遞給某個(gè)方法的選項(xiàng)表示為枚舉類型,而多個(gè)選項(xiàng)又可同時(shí)使用银亲,那么就將各選項(xiàng)值定義為 2 的冪慢叨,以便通過(guò)按位或操作將其組合起來(lái)。
- 用 NS_ENUM 與 NS_OPTIONS 宏來(lái)定義枚舉類型务蝠,并指明其底層數(shù)據(jù)類型拍谐。這樣就可以確保枚舉是用開(kāi)發(fā)者所選的底層數(shù)據(jù)類型實(shí)現(xiàn)出來(lái)的,而不是采用編譯器所選的類型践瓷。
- 在處理枚舉類型的 switch 語(yǔ)句中不要實(shí)現(xiàn) default 分支亡蓉。這樣加入新的枚舉值之后崖面,編譯器就會(huì)發(fā)出警告提示梯影,switch 還有未處理的枚舉值甲棍。
類及其方法命名
Objective-C 沒(méi)有其他語(yǔ)言那種內(nèi)置的命名空間(namespace)機(jī)制感猛。因此奢赂,我們?cè)谄鹈麜r(shí)要設(shè)法避免潛在的命名沖突膳灶,否則很容易就重名了序厉。避免此問(wèn)題的唯一方法就是變相實(shí)現(xiàn)命名空間: 為所有名稱都加上適當(dāng)前綴毕箍。所選前綴可以是與公司而柑、應(yīng)用程序或者二者皆有關(guān)聯(lián)的名字媒咳。使用 Cocoa 和 Cocoa Touch 創(chuàng)建應(yīng)用程序時(shí)一定要注意,Apple 宣傳其保留使用所有"兩個(gè)字母前綴"(tow-letter prefixed)的權(quán)利恨搓。所以你自己選用的前綴應(yīng)該是三個(gè)字母的常拓。你可以在蘋果官網(wǎng) Class Names Must Be Unique Across an Entire App 看到上述說(shuō)明:
In order to keep class names unique, the convention is to use prefixes on all classes. You’ll have noticed that Cocoa and Cocoa Touch class names typically start either with NS or UI. Two-letter prefixes like these are reserved by Apple for use in framework classes.
Your own classes should use three letter prefixes. These might relate to a combination of your company name and your app name, or even a specific component within your app.
You should also name your classes using a noun that makes it clear what the class represents, like these examples from Cocoa and Cocoa Touch:
| NSWindow | CAAnimation | NSWindowController | NSManagedObjectContext
另外弄抬,在文檔 Coding Guidelines for Cocoa 中提到:
Use prefixes when naming classes, protocols, functions, constants, and typedef structures. Do not use prefixes when naming methods; methods exist in a name space created by the class that defines them. Also, don’t use prefixes for naming the fields of a structure.
這里需要說(shuō)明兩點(diǎn):
- 上述所說(shuō)的
functions
指的是純 C 函數(shù)。對(duì)于純 C 函數(shù)和全局變量懊亡,不論其處于頭文件或者其實(shí)現(xiàn)文件中乎串,在編譯好的目標(biāo)文件中叹誉,這些名稱要算作"頂級(jí)符號(hào)"(top-level symbol)的钧唐。因此我們總應(yīng)該為這種 C 函數(shù)的名字加上前綴匠襟。通常情況下宅此,這類 C 函數(shù)我們可以以定義其類的名字作為前綴父腕。這樣璧亮,在調(diào)試時(shí)枝嘶,若此符號(hào)出現(xiàn)在棧回溯信息中及刻,則很容易就能判明問(wèn)題源自哪塊代碼缴饭。例如:
ACLSoundPlayer.h
#import <Foundation/Foundation.h>
@interface ACLSoundPlayer : NSObject
@end
ACLSoundPlayer.m
#import "ACLSoundPlayer.h"
#import <AudioToolbox/AudioToolbox.h>
void ACLSoundPlayerCompletion(SystemSoundID ssID, void *clientData) {
}
@implementation ACLSoundPlayer
@end
- 上述引用說(shuō)到不應(yīng)該為 Objective-C methods 添加前綴颗搂,我覺(jué)得應(yīng)該添加一個(gè)例外丢氢,當(dāng)我們定義分類中的方法時(shí)疚察,則總應(yīng)該為其添加前綴稍浆,這會(huì)在下一條詳細(xì)說(shuō)明衅枫。
最后一種情況弦撩,若為第三庫(kù)編寫自己的代碼益楼,并準(zhǔn)備將其發(fā)布為程序庫(kù)供他人開(kāi)發(fā)應(yīng)用程序所用時(shí)感凤,你應(yīng)該給你所用的那份第三方庫(kù)代碼都加上你自己的前綴。為便于說(shuō)明屠橄,假如你要發(fā)布的程序庫(kù)叫 EOCLibrary礁哄,你所使用的第三方庫(kù)叫 XYZLibrary桐绒,則你應(yīng)該把你使用的 XYZLibrary 中所有名字都冠以 EOC
, 成為 EOCXYZLibrary
, 原因如下:
- 你的程序所包含的那個(gè)第三方庫(kù)也許還會(huì)為應(yīng)用程序本身所引入。
- 你可能會(huì)想劳翰,讓應(yīng)用程序本身不要直接引入 XYZLibrary佳簸,改用 EOCLibrary 中使用的那個(gè)生均,但是马胧,應(yīng)用程序也許還會(huì)引入另一個(gè)名為 ABCLibrary 的第三方庫(kù)佩脊,而該庫(kù)中又包含了 XYZLibrary威彰。此時(shí)歇盼,如果你和 ABCLibrary 的作者都不給各自所用的 XYZLibrary 加前綴豹缀,那么應(yīng)用程序依然會(huì)出現(xiàn)重復(fù)符號(hào)錯(cuò)誤邢笙。
- 你的庫(kù)里所引用的第三方庫(kù)是 X 版本的,而應(yīng)用程序卻需要引用第三庫(kù)的 Y 版本的功能筐骇。
對(duì)于類中的方法命名,應(yīng)遵循以下規(guī)則:
- 如果方法的返回值是新建的江滨,那么方法名的首個(gè)詞應(yīng)是返回值的類型铛纬,例如:
+stringWithString:
。除非前面還有修飾語(yǔ)唬滑,例如 localizedString告唆。屬性的存取方法不遵循這種命名方式棺弊。 - BOOL屬性應(yīng)加 is 前綴。如果某方法返回非屬性的 Boolean 值擒悬,那么應(yīng)該根據(jù)你功能模她,選用 has 或 is 當(dāng)前綴。
分類及其方法命名
分類機(jī)制通常用于向無(wú)源碼的既有類中新增功能懂牧。分類中的方法是直接加在類里面的躯保,它們就好比這個(gè)類固有的方法,將分類方法加入類中這一操作是在運(yùn)行期系統(tǒng)加載分類時(shí)完成的振惰。運(yùn)行期系統(tǒng)會(huì)把分類中所實(shí)現(xiàn)的每個(gè)方法都加入類的方法列表中桶蛔。如果類中本來(lái)就有此方法碟婆,而分類中又實(shí)現(xiàn)了一次公给,那么分類中的方法會(huì)覆蓋原來(lái)那一份實(shí)現(xiàn)代碼。實(shí)際上可能會(huì)發(fā)生很多次覆蓋加叁,比如某個(gè)分類中的方法覆蓋了"主實(shí)現(xiàn)"中的相關(guān)方法豫柬,而另外一個(gè)分類中的方法又覆蓋了這個(gè)分類中的方法础嫡,多次覆蓋的結(jié)果以最后一個(gè)分類為準(zhǔn)晚唇。當(dāng)有多份實(shí)現(xiàn)時(shí)闽瓢,無(wú)法確定運(yùn)行時(shí)使用的是那份實(shí)現(xiàn)届谈。由這樣引發(fā)的 bug 很難追查摔吏。
那么诗箍,如何來(lái)最大限度的避免這種覆蓋呢?
在 iOS 開(kāi)發(fā)中塑顺,沒(méi)有命名空間的概念糙俗。通常劈彪,我們通過(guò)為項(xiàng)目中所有類命名加上特有的前綴來(lái)實(shí)現(xiàn)命名空間的功能,來(lái)避免可能發(fā)生的與使用第三方庫(kù)中的方法命名相同。同樣的冬骚,這里我們也是為分類只冻,以及分類中的所有方法都加上特定前綴喜德。這個(gè)前綴應(yīng)該與應(yīng)用程序庫(kù)中其他地方所用的前綴相同,通常會(huì)使用公司名或應(yīng)用程序名來(lái)做決定馁蒂。例如來(lái)編寫一個(gè)判斷對(duì)象是否為空的分類方法:
NSObject+ACLNetworkingMethods.h
@interface NSObject (ACLNetworkingMethods)
- (BOOL)acl_isEmptyObject;
@end
這里為分類名以及分類中的方法加上了 ACL
的前綴。注意在方法中,需前綴小寫辑舷。
另外,使用分類時(shí)刚陡,不要刻意覆寫分類中的方法惩妇,尤其是當(dāng)你把代碼發(fā)布為程序庫(kù)供其他開(kāi)發(fā)者使用時(shí),因?yàn)槟銦o(wú)法決定其他開(kāi)發(fā)者需要的是方法哪份實(shí)現(xiàn)筐乳。
總結(jié):
- 向第三方類中添加分類時(shí)歌殃,總應(yīng)該給分類名稱加上你專有的前綴,前綴須大寫蝙云。
- 向第三方類中添加分類時(shí)氓皱,總應(yīng)該給分類中的方法名加上你專有的前綴,前綴須小寫勃刨,且以下劃線連接前綴與方法名波材。
歡迎留言交流。