談?wù)刬OS中的命名規(guī)范

轉(zhuǎn)載: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)有的東西。

<a id="ConstantNamed"></a>常量命名

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
EOCViewClass.hextern 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;

<a id="Status_opation_statusCode"></a>用枚舉表示狀態(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 還有未處理的枚舉值。

<a id="Class_method"></a>類及其方法命名

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)在?;厮菪畔⒅校瑒t很容易就能判明問(wèn)題源自哪塊代碼。例如:
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
  • 上述引用說(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)前綴。

<a id="Category_method"></a>分類及其方法命名

分類機(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)該給分類中的方法名加上你專有的前綴,前綴須小寫,且以下劃線連接前綴與方法名。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容