轉載:http://liumh.com/
本文談談 iOS 開發中的命名規范,主要涉及常量命名、枚舉命名、類及其方法命名,以及分類及其方法命名。如果你找的是官網的編碼規范,請移步: Coding Guidelines for Cocoa。當然本文會講一些官網沒有的東西。
<a id="ConstantNamed"></a>常量命名
iOS 開發中,肯定避免不了要命名一些常量,那么,我們應該怎樣來命名常量呢?在討論上述問題前,先來了解定義常量的兩種方式。
- 第一種,使用 #define預處理定義常量。例如:
#define ANIMATION_DURATION 0.3
定義一個 ANIMATION_DURATION
常量來表示 UI 動畫的一個常量時間,這樣,代碼中所有使用ANIMATION_DURATION
的地方都會被替換成 0.3,但是這樣定義的常量無類型信息,且如果
你在調試時想要查看 ANIMATION_DURATION
的值卻無從下手,因為在預處理階段ANIMATION_DURATION
就已經被替換了,不便于調試。因此,棄用這種方式定義常量。
- 第二種,使用類型常量。將上面的預處理定義常量修改成類型常量:
static const NSTimeInterval kAnimationDuration = 0.3;
這樣就為常量帶入了類型信息,那么定義類型常量又有什么規范呢?
對于局限于某編譯單元(實現文件)的常量,通常以字符k
開頭,例如上文中的 kAnimationDuration
,且需要以 static const 修飾,例如:
static const NSTimeInterval kAnimationDuration = 0.3;
對于定義于類頭文件的常量,外部可見,則通常以定義該常量所在類的類名開頭,例如 EOCViewClassAnimationDuration
, 仿照蘋果風格,在頭文件中進行 extern 聲明,在實現文件中定義其值:
EOCViewClass.h
extern const NSTimeInterval EOCViewClassAnimationDuration;
EOCViewClass.m
const NSTimeInterval EOCViewClassAnimationDuration = 0.3;
對于字符串常量,則會像這樣:
EOCViewClass.h
EOCViewClass.hextern NSString *const EOCViewClassStringConstant;
EOCViewClass.m
NSString *const EOCViewClassStringConstant = @"EOCStringConstant";
常量定義是從右往左解讀,上面的示例中就是定義了一個常量指針,其指向一個 NSString 對象, 這樣該常量就不會被隨意修改。至于這種暴露出來的類常量最前面是否加上字母k, 可以根據自己習慣在團隊中進行約定,因為從 iOS 的接口中我看到這兩種情況都有, 如
NSString *const UIApplicationLaunchOptionsRemoteNotificationKey;
NSString *const UIApplicationLaunchOptionsLocalNotificationKey;
NSString *const kCAAnimationCubic;
NSString *const kCAAnimationCubicPaced;
<a id="Status_opation_statusCode"></a>用枚舉表示狀態、選項、狀態碼
項目中,可用枚舉來表示一系列的狀態、選項和狀態碼。例如 iOS SDK 中表示 UICollectionView 滑動方向的枚舉定義如下:
typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) {
UICollectionViewScrollDirectionVertical,
UICollectionViewScrollDirectionHorizontal
};
或者定義 UITableView Style 的枚舉:
typedef NS_ENUM(NSInteger, UITableViewStyle) {
UITableViewStylePlain, // regular table view
UITableViewStyleGrouped // preferences style table view
};
從這里可以看出,定義的枚舉類型名稱應以 2~3 個大寫字母開頭,而這通常與項目設置的類文件前綴相同,跟隨其后的命名應采用駝峰命名法則,命名應準確表述枚舉表示的意義,枚舉中各個值都應以定義的枚舉類型開頭,其后跟隨各個枚舉值對應的狀態、選項或者狀態碼。對于需要以按位或操作來組合的枚舉都應使用 NS_OPTIONS 宏來定義,例如 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
};
這樣定義的選項能夠以 按位或操作符 來進行組合,枚舉中每個值均可啟用或者禁用某一選項,在使用時,可以使用 按位與操作符 來檢測是否啟用了某一選項,如下:
UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
if (resizing & UIViewAutoresizingFlexibleWidth) {//
UIViewAutoresizingFlexibleWidth is set
};
另外,我們可能使用 switch 語句時,會在最后加上 default 分支,但是若用枚舉定義狀態機,則最好不要使用 default 分支,因為如果稍后再加了一種狀態,那么編譯器就會發出警告,提示新加入的狀態并未在 switch 分支中處理。假如寫上了 default 分支,那么它就會處理這個新狀態,從而導致編譯器不發出警告,用 NS_ENUM 定義其他枚舉類型時也要注意此問題。例如在定義代表 UI 元素樣式的枚舉時,通常要確保 switch 語句能正確處理所有樣式。
小結一下:
- 應該用枚舉來表示狀態機的狀態、傳遞給方法的選項以及狀態碼等值,給這些值起個易懂的名字。
- 如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用,那么就將各選項值定義為 2 的冪,以便通過按位或操作將其組合起來。
- 用 NS_ENUM 與 NS_OPTIONS 宏來定義枚舉類型,并指明其底層數據類型。這樣就可以確保枚舉是用開發者所選的底層數據類型實現出來的,而不是采用編譯器所選的類型。
- 在處理枚舉類型的 switch 語句中不要實現 default 分支。這樣加入新的枚舉值之后,編譯器就會發出警告提示,switch 還有未處理的枚舉值。
<a id="Class_method"></a>類及其方法命名
Objective-C 沒有其他語言那種內置的命名空間(namespace)機制。因此,我們在起名時要設法避免潛在的命名沖突,否則很容易就重名了。避免此問題的唯一方法就是變相實現命名空間: 為所有名稱都加上適當前綴。所選前綴可以是與公司、應用程序或者二者皆有關聯的名字。使用 Cocoa 和 Cocoa Touch 創建應用程序時一定要注意,Apple 宣傳其保留使用所有“兩個字母前綴”(tow-letter prefixed)的權利。所以你自己選用的前綴應該是三個字母的。你可以在蘋果官網 Class Names Must Be Unique Across an Entire App 看到上述說明:
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.
這里需要說明兩點:
- 上述所說的functions指的是純 C 函數。對于純 C 函數和全局變量,不論其處于頭文件或者其實現文件中,在編譯好的目標文件中,這些名稱要算作“頂級符號”(top-level symbol)的。因此我們總應該為這種 C 函數的名字加上前綴。通常情況下,這類 C 函數我們可以以定義其類的名字作為前綴。這樣,在調試時,若此符號出現在棧回溯信息中,則很容易就能判明問題源自哪塊代碼。例如:
ACLSoundPlayer.h
#import <Foundation/Foundation.h>
@interface ACLSoundPlayer :NSObject
@endACLSoundPlayer.m
#import "ACLSoundPlayer.h"
#import <AudioToolbox/AudioToolbox.h>
void ACLSoundPlayerCompletion(SystemSoundID ssID, void *clientData) {
}
@implementation ACLSoundPlayer
@end
- 上述引用說到不應該為 Objective-C methods 添加前綴,我覺得應該添加一個例外,當我們定義分類中的方法時,則總應該為其添加前綴,這會在下一條詳細說明。
最后一種情況,若為第三庫編寫自己的代碼,并準備將其發布為程序庫供他人開發應用程序所用時,你應該給你所用的那份第三方庫代碼都加上你自己的前綴。為便于說明,假如你要發布的程序庫叫 EOCLibrary,你所使用的第三方庫叫 XYZLibrary,則你應該把你使用的 XYZLibrary 中所有名字都冠以 EOC
, 成為 EOCXYZLibrary, 原因如下:
- 你的程序所包含的那個第三方庫也許還會為應用程序本身所引入。
- 你可能會想,讓應用程序本身不要直接引入 XYZLibrary,改用 EOCLibrary 中使用的那個,但是,應用程序也許還會引入另一個名為 ABCLibrary 的第三方庫,而該庫中又包含了 XYZLibrary。此時,如果你和 ABCLibrary 的作者都不給各自所用的 XYZLibrary 加前綴,那么應用程序依然會出現重復符號錯誤。
- 你的庫里所引用的第三方庫是 X 版本的,而應用程序卻需要引用第三庫的 Y 版本的功能。
對于類中的方法命名,應遵循以下規則:
- 如果方法的返回值是新建的,那么方法名的首個詞應是返回值的類型,例如: +stringWithString:。除非前面還有修飾語,例如 localizedString。屬性的存取方法不遵循這種命名方式。
- BOOL屬性應加 is 前綴。如果某方法返回非屬性的 Boolean 值,那么應該根據你功能,選用 has 或 is 當前綴。
<a id="Category_method"></a>分類及其方法命名
分類機制通常用于向無源碼的既有類中新增功能。分類中的方法是直接加在類里面的,它們就好比這個類固有的方法,將分類方法加入類中這一操作是在運行期系統加載分類時完成的。運行期系統會把分類中所實現的每個方法都加入類的方法列表中。如果類中本來就有此方法,而分類中又實現了一次,那么分類中的方法會覆蓋原來那一份實現代碼。實際上可能會發生很多次覆蓋,比如某個分類中的方法覆蓋了“主實現”中的相關方法,而另外一個分類中的方法又覆蓋了這個分類中的方法,多次覆蓋的結果以最后一個分類為準。當有多份實現時,無法確定運行時使用的是那份實現。由這樣引發的 bug 很難追查。
那么,如何來最大限度的避免這種覆蓋呢?
在 iOS 開發中,沒有命名空間的概念。通常,我們通過為項目中所有類命名加上特有的前綴來實現命名空間的功能,來避免可能發生的與使用第三方庫中的方法命名相同。同樣的,這里我們也是為分類,以及分類中的所有方法都加上特定前綴。這個前綴應該與應用程序庫中其他地方所用的前綴相同,通常會使用公司名或應用程序名來做決定。例如來編寫一個判斷對象是否為空的分類方法:
NSObject+ACLNetworkingMethods.h
@interface NSObject (ACLNetworkingMethods)
- (BOOL)acl_isEmptyObject;
@end
這里為分類名以及分類中的方法加上了 ACL
的前綴。注意在方法中,需前綴小寫。
另外,使用分類時,不要刻意覆寫分類中的方法,尤其是當你把代碼發布為程序庫供其他開發者使用時,因為你無法決定其他開發者需要的是方法哪份實現。
總結:
- 向第三方類中添加分類時,總應該給分類名稱加上你專有的前綴,前綴須大寫。
- 向第三方類中添加分類時,總應該給分類中的方法名加上你專有的前綴,前綴須小寫,且以下劃線連接前綴與方法名。