@(〓〓 iOS-實(shí)用技術(shù))[Objective-C編碼規(guī)范]
- 作者: Liwx
- 郵箱: 1032282633@qq.com
目錄
- 11.Objective-C 編碼規(guī)范指南
- 項(xiàng)目工程結(jié)構(gòu)
- 代碼結(jié)構(gòu)
- 項(xiàng)目Xode相關(guān)配置
- 代碼縮進(jìn)配置
- 代碼行號(hào)顯示配置
- 項(xiàng)目編碼建議
- 注釋
- 方法注釋
- 屬性注釋
- 命名與編碼規(guī)范
- 類名命名規(guī)則
- 協(xié)議編碼規(guī)則
- 控件/控制器定義命名規(guī)則
- 方法
- 方法名和參數(shù)命名規(guī)則
- 方法聲明和編碼規(guī)范
- 方法調(diào)用父類方法編碼規(guī)則
- 函數(shù)
- 變量
- 成員屬性編碼規(guī)則
- 常量
- Foundation框架常量賦值規(guī)則
- 定義普通常量以字母k開頭
- 枚舉與宏定義
- 枚舉編碼規(guī)則
- NS_ENUM定義普通枚舉
- NS_OPTIONS定義位枚舉
- 通知和異常
- 通知NSNotification常量命名規(guī)則
- 異常命名規(guī)則
- 布爾值
- 條件語句
- if else 條件語句
- switch case 語句
- 其他規(guī)則
- 初始化方法(構(gòu)造方法)規(guī)則
- 直接放回對(duì)應(yīng)數(shù)據(jù)時(shí),無需添加get, calc單詞
- 單例的聲明和使用規(guī)則
- 設(shè)置常用屬性,直接使用點(diǎn)語法
- 協(xié)議和代理方法命名規(guī)則
- 可以使用({})語法模塊化設(shè)置控件屬性(可選)
項(xiàng)目工程結(jié)構(gòu)
代碼結(jié)構(gòu)
-
實(shí)現(xiàn)文件中的代碼結(jié)構(gòu),提倡以下約定:
- 用
#pragma mark -將函數(shù)或方法按功能進(jìn)行分組;分組之間空2行,方法之間空1行. - delgate或協(xié)議相關(guān)方法放到一般內(nèi)容之后。
- 用
#pragma mark - Lifecycle (生命周期)
- (void)dealloc {}
- (instancetype)init {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}
#pragma mark - Private (私有方法,比如初始控件設(shè)置方法/類內(nèi)部業(yè)務(wù)處理方法)
- (CGFloat)setupTableView {}
#pragma mark - Public (對(duì)外公開方法)
- (CGFloat)reloadAllData {}
#pragma mark - Network (加載網(wǎng)絡(luò)數(shù)據(jù))
- (void)loadMoreData {}
- (void)loadNewData {}
- (void)loadOtherData {}
#pragma mark - Property Setter/Getter (成員屬性的setter和getter方法)
- (void)setCustomProperty:(id)value {}
- (id)customProperty {}
#pragma mark - Events (UIControl響應(yīng)函數(shù),如按鈕點(diǎn)擊事件)
- (void)saveButtonClick:(UIButton *)saveButton {}
#pragma mark - KVO/Notification (KVO/通知響應(yīng)函數(shù))
- (void)dataSourceRefreshNotification:(NSNotification *)notification {}
- (void)observeValueForKeyPath:(nullable NSString *)keyPath ofObject:(nullable id)object change:(nullable NSDictionary<NSString*, id> *)change context:(nullable void *)context {}
#pragma mark - Protocol/Delegate 如UITableViewDataSource/UITableViewDelegate (協(xié)議和代理)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {}
#pragma mark - LazyLoad (懶加載)
- (UILabel *)nameLabel {}
#pragma mark - Other (其他)
- (id)copyWithZone:(NSZone *)zone {}
- (NSString *)description {}
項(xiàng)目Xode相關(guān)配置
代碼縮進(jìn)配置
- 只用
空格縮進(jìn),1個(gè)TAB = 4個(gè)空格字符
在
XCode->Preferences->Text Editing->Indentation中進(jìn)行如下設(shè)置:
ps. 設(shè)置成4個(gè),是因?yàn)閄code的默認(rèn)縮進(jìn)是4個(gè)空格。

代碼行號(hào)顯示配置
- Xcode行號(hào)顯示配置
勾選上
XCode->Preferences->Text Editing->Editing中的Line numbers,開啟行號(hào)提示。

項(xiàng)目編碼建議
-
建議:每行代碼的長(zhǎng)度最多
不超過120個(gè)字符;
勾選
XCode->Preferences->Text Editing->Editing,并將長(zhǎng)度設(shè)置成120個(gè)字符來打開行寬指示。設(shè)置成功時(shí)Xcode會(huì)出現(xiàn)一條豎線! (可選配置)
建議:為了簡(jiǎn)潔和便于閱讀,請(qǐng)嘗試將單個(gè)函數(shù)或方法的實(shí)現(xiàn)代碼控制在
50行內(nèi);單個(gè)實(shí)現(xiàn)文件里的代碼行數(shù)控制在500~600行內(nèi);建議:為了簡(jiǎn)潔和便于閱讀,請(qǐng)嘗試將單個(gè)函數(shù)或方法的實(shí)現(xiàn)代碼控制在
50行內(nèi);當(dāng)接近或超過800行時(shí),就應(yīng)當(dāng)開始考慮分割實(shí)現(xiàn)文件了。
注釋
方法注釋
- 方法注釋規(guī)則
- 注釋應(yīng)該盡量保持
簡(jiǎn)潔,代碼應(yīng)該盡量達(dá)到能自我解釋的程度; - 注釋必須和代碼保持同步。不要出現(xiàn)代碼修改了,注釋不更新的情況;
-
對(duì)外公開方法 使用
/** 方法描述 */或VVDocumenter-Xcode注釋,公開方法需盡量注釋清楚;如果公開方法有帶參數(shù),可按需對(duì)參數(shù)進(jìn)行說明; -
其他方法可使用
// 方法描述或VVDocumenter-Xcode注釋; - 方法內(nèi)部
行注釋需對(duì)齊;
- 注釋應(yīng)該盡量保持
// .m文件
// 按鈕點(diǎn)擊事件處理 (非對(duì)外公開的方法)
- (void)saveButtonClick:(UIButton *)saveButton {
NSLog(@"Hello Liwx"); // 打印日志
NSLog(@"Hello Liwx"); // 打印日志
}
/** 刷新全部數(shù)據(jù) (對(duì)外公開的方法)*/
- (void)reloadAllData {
//Do Something
}
// .h文件
/** 刷新全部數(shù)據(jù) (對(duì)外公開的方法)*/
- (void)reloadAllData;
屬性注釋
建議
外部屬性注釋建議使用/** 屬性描述 */注釋, 因?yàn)閄code對(duì)此類注釋有提示功能,便于開發(fā)人員在編碼時(shí)能更快的了解該屬性的作用;建議屬性與
@interface ... @end之間各空1行;
@interface ViewController ()
/** 時(shí)間 */
@property (nonatomic, copy) NSString *time;
@end

- 私有屬性注釋可使用
// 屬性描述或/** 屬性描述 */注釋;
命名與編碼規(guī)范
類名命名規(guī)則
- 類名命名規(guī)則
類名、類別名字及協(xié)議名字,都采用
大駝峰式命名規(guī)則-
文件名要能反映出它所包含的類的名稱
如:NSString.h 和 NSString.m 包含了NSString類的定義和實(shí)現(xiàn)
-
Category的文件名要包含它所擴(kuò)展的那個(gè)類的名稱,并且類別名稱要盡量能夠描述它的功能
UIImage+Resize.h 或 UIImage+TintColor.h
-
在面向特定應(yīng)用的代碼中,類名盡量避免使用前綴,每個(gè)類都使用相同的前綴會(huì)影響可讀性
面向特定應(yīng)用的代碼,指那些只會(huì)在一個(gè)項(xiàng)目中使用的代碼,不會(huì)被用于其他項(xiàng)目中的代碼。
-
在面向多應(yīng)用的代碼中,類名要使用前綴,防止命名沖突
面向多應(yīng)用的代碼,指那些會(huì)被多個(gè)項(xiàng)目共同使用的代碼。
比如CRKit這個(gè)類庫(kù)中,使用了CR前綴。
-
建議:前綴至少使用三個(gè)字母
此條是為了減少命名沖突。但鑒于目前流行前綴大多都是兩個(gè)字母,所以此條不做強(qiáng)制要求
協(xié)議編碼規(guī)則
- 協(xié)議編碼規(guī)則
- 協(xié)議聲明或定義中,類型標(biāo)識(shí)符、協(xié)議名稱、尖括號(hào)之間
不留空格;
- 協(xié)議聲明或定義中,類型標(biāo)識(shí)符、協(xié)議名稱、尖括號(hào)之間
// 協(xié)議聲明協(xié)議名稱、尖括號(hào)之間不留空格
@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>
@end
// 定義屬性遵守協(xié)議,不留空格
@property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate;
控件/控制器定義命名規(guī)則
- 控件命名盡量使用控件全稱命名,
不建議使用縮寫;命名規(guī)則: [功能(name)]+[控件名(Label)]
// 建議使用控件全稱命名
/** 名稱Label */
@property (nonatomic, weak) UILabel *nameLabel;
/** 保存Button */
@property (nonatomic, weak) UIButton *saveButton;
// 不建議控件名使用縮寫
/** 名稱Label */
@property (nonatomic, weak) UILabel *nameLbl;
/** 保存Button */
@property (nonatomic, weak) UIButton *saveBtn;
- 定義或聲明類時(shí)如果名稱過長(zhǎng)采用
后綴縮寫;注意大小寫;- 如控制器
ViewController可縮寫為Vc; -
TableViewCell可縮寫為TvCell; -
CollectionViewCell可縮寫為CvCell;
- 如控制器
// TableViewCell后綴縮寫為TvCell
TopicTableViewCell *topicTvCell = nil;
// CollectionViewCell后綴縮寫為CvCell
ItemCollectionViewCell *itemCvCell = nil;
// ViewController可縮寫為Vc
HomeViewController *homeVc = nil;
方法
方法名和參數(shù)命名規(guī)則
- 方法名和參數(shù)名都采用
小駝峰式命名規(guī)則;
如:- (BOOL)isFileExistedAtPath:(NSString *)filePath;
- 方法名可以使?用情態(tài)動(dòng)詞(
can,should,will等)來提?高清晰性,但不要使?用 do 或 does;
// 正確
- (BOOL)canHide;
- (BOOL)shouldRefreshData;
- (void)willChangeData
方法聲明和編碼規(guī)范
- 方法聲明中,
-/+和返回值類型之間要空1個(gè)空格,方法名和參數(shù)類型之間以及參數(shù)類型和參數(shù)名之間不留空格;
- (instancetype)initwithTitle:(NSString *)title; // 正確
-(instancetype)initwithTitle:(NSString *)title; // 錯(cuò)誤
- (instancetype) initwithTitle:(NSString *)title; // 錯(cuò)誤
- (instancetype)initwithTitle: (NSString *)title; // 錯(cuò)誤
- (instancetype)initwithTitle:(NSString *) title; // 錯(cuò)誤
- 方法名和參數(shù)名應(yīng)該盡量讀起來像一句話。
如:convertPoint:fromRect: 或者 replaceCharactersInRange:withString:
- 當(dāng)各個(gè)參數(shù)是接收者的某個(gè)屬性時(shí),方法名中不要用
"and"來連接;
// 正確
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
// 錯(cuò)誤
- (instancetype)initWithNibName:(NSString *)nibNameOrNil andBundle:(NSBundle *)nibBundleOrNil;
- 方法名之后空
1格,緊隨左大括號(hào){,無需換號(hào);但是右大括號(hào)}必須另取一行
- (CGFloat)setupTableView {
// Do Something
}
方法調(diào)用父類方法編碼規(guī)則
- 重載父類方法時(shí),遇到必須調(diào)用父類方法時(shí)。調(diào)用super的代碼和重載的代碼之間留一行空行。將super方法的調(diào)用和重載代碼區(qū)隔開來.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Do Something
}
函數(shù)
-
函數(shù)指純C函數(shù),這里提倡與蘋果風(fēng)格類似的約定。
函數(shù)名采用大駝峰式命名方式,``參數(shù)名采用小駝峰式`命名方式如果函數(shù)和某個(gè)特定類型相關(guān),那么函數(shù)名前綴要和類型前綴一樣
如
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)等
變量
成員屬性編碼規(guī)則
-
屬性名和變量名都采用小駝峰式命名規(guī)則; -
禁止使用匈牙利標(biāo)記法或含糊不清的縮寫單詞來命名變量; - 指針符號(hào)
"*" 靠近變量名字。(常量定義除外) - property屬性
括號(hào)兩邊各空1格, 屬性關(guān)鍵字以逗號(hào)加空格隔開 -
除了xib拖控件的方式除外,nonatomic需放在最前面, strong,weak,assign,copy應(yīng)放在nonatomic后面; - 成員變量和局部變量聲明Demo
// 成員變量聲明
/** 標(biāo)題Label (正確) */
@property (nonatomic, copy) NSString *titleLabel;
/** 標(biāo)題Label (錯(cuò)誤) */
@property (nonatomic, copy) NSString *titleLbl;
/** 標(biāo)題Label (錯(cuò)誤) */
@property(nonatomic, copy) NSString *titleLabel;
/** 標(biāo)題Label (錯(cuò)誤) */
@property (nonatomic,copy) NSString *titleLabel;
/** 標(biāo)題Label (錯(cuò)誤) */
@property (copy, nonatomic) NSString *titleLabel;
// 局部變量聲明
NSString *titleLabel = nil; // 正確
NSString* titleLabel = nil; // 錯(cuò)誤
NSString * titleLabel = nil; // 錯(cuò)誤
NSString*titleLabel = nil; // 錯(cuò)誤
- 使用property時(shí),優(yōu)先使用點(diǎn)語法;
/** 名稱 */
@property (nonatomic, copy) NSString *name;
// 訪問成員屬性時(shí),優(yōu)先使用點(diǎn)語法
self.name = @"Liwx";
- 賦值操作符
"="兩邊各空1格;
self.name = @"Liwx"; // 正確
self.name= @"Liwx"; // 錯(cuò)誤
self.name=@"Liwx"; // 錯(cuò)誤
- 如果使用property修飾的是屬性BOOL值,建議為getter方法加上一個(gè)
"is"開頭的別名。
@property (assign, getter = isSelected) BOOL selected;
- 如果
網(wǎng)絡(luò)獲取的屬性數(shù)據(jù)為數(shù)值型的,則定義屬性也應(yīng)該為數(shù)值型;如果是枚舉類型,則應(yīng)定義對(duì)應(yīng)枚舉類型;不應(yīng)將數(shù)值型定義為NSString字符串類型;
// 正確
@property (copy,nonatomic) UserStatus *userStatus;
// 錯(cuò)誤
@property (copy,nonatomic) NSString *userStatus;
常量
Foundation框架常量賦值規(guī)則
- 為了提高代碼簡(jiǎn)潔度,創(chuàng)建NSString, NSDictionary, NSArray, 以及NSNumber等常量時(shí),使用Literals語法;
// 正確
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
// 錯(cuò)誤
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *zipCode = [NSNumber numberWithInteger:10018];
定義普通常量以字母k開頭
- 如果
定義的常量是類,則格式為:[類名] + [*] + [const] + [k+常量描述]; - 如果
定義的常量屬于基本數(shù)據(jù)類型, 則格式為:[基本數(shù)據(jù)類型] + [const] + [k+常量描述];
// 普通常量定義
NSString * const kUserKey = @"kUserKey";
CGFloat const kTopViewHeight = 50;
- 只在某一個(gè)特定文件里面使用的常量,用
static
static關(guān)鍵字保證變量只有文件作用域,可以避免變量名重名造成的鏈接錯(cuò)誤問題。
如: static CGFloat const RWImageThumbnailHeight = 50.0;
枚舉與宏定義
枚舉編碼規(guī)則
- 定義枚舉常量時(shí),使用
NS_ENUM或NS_OPTIONS;
因?yàn)?strong>NS_ENUM和NS_OPTIONS都提供了類型檢查;
- 定義枚舉時(shí),
一定要注釋,并且格式為:[枚舉類型名(UITableViewStyle)] + [類型(Plain)];
NS_ENUM定義普通枚舉
- 使用
NS_ENUM定義普通枚舉Demo,建議枚舉值 = 對(duì)應(yīng)數(shù)值
// 應(yīng)用皮膚樣式
typedef NS_ENUM(NSInteger, AppStyle) {
AppStyleLight = 0, // 白天模式
AppStyleDark = 1 // 夜間模式
};
NS_OPTIONS定義位枚舉
- 使用
NS_OPTIONS定義位枚舉
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0, // 注釋
UIViewAutoresizingFlexibleLeftMargin = 1 << 0, // 注釋
UIViewAutoresizingFlexibleWidth = 1 << 1, // 注釋
...
};
通知和異常
通知NSNotification常量命名規(guī)則
- 通知常量命名格式: [相關(guān)聯(lián)的類名字] + [Did | Will] + [獨(dú)一無二的一段名稱] + Notification
-
建議:
定義通知常量不采用宏定義的方法; - .h文件, 全局通知聲明時(shí)需加上UIKIT_EXTERN關(guān)鍵字
- .m文件, 通知常量名和值必須保持一致
-
建議:
- 通知命名Demo
// .h文件
// 全局通知聲明時(shí)需加上UIKIT_EXTERN關(guān)鍵字
UIKIT_EXTERN NSString *const UIKeyboardDidChangeFrameNotification ;
// .m文件, 通知常量名和值必須保持一致
NSString *const UIKeyboardDidChangeFrameNotification = @"UIKeyboardDidChangeFrameNotification";
異常命名規(guī)則
- 異常名字的命名規(guī)則:[前綴] + [獨(dú)一無二的一段名稱] + Exception
如:NSColorListIOException
布爾值
- Objective-C的布爾值只使用
YES和NO; -
注意:true和false只能用于CoreFoundation,C或C++的代碼中; - 禁止將某個(gè)值或表達(dá)式的結(jié)果與YES進(jìn)行比較
// 正確
if (self.isLogin) {}
// 錯(cuò)誤
if (self.isLogin == YES) {}
- 如果返回值為BOOL值,必須確保返回值為YES或NO,最好不要存在多值的情況
// 正確
- (BOOL)isLogin {
return self.userToken.length != 0 ? YES : NO;
}
// 錯(cuò)誤
- (BOOL)isLogin {
return self.userToken.length;
}
條件語句
if else 條件語句
- 條件語句的語句體,即便只有一行,也
不能省略花括弧; - 判斷條件之后空
1格,緊隨左大括號(hào){,無需換號(hào);但是右大括號(hào)}必須另取一行; - else或 elseif應(yīng)緊隨在右大括號(hào)
}之后,并且中間空1格;
// 正確
if (isLogin) {
// Do Something
} else {
// DO Something
}
// 錯(cuò)誤
if (isLogin)
{
// Do Something
}
else {
// DO Something
}
// 正確
if (error == nil) {
return success;
}
// 錯(cuò)誤
if (error == nil)
return success;
- 多層嵌套的條件語句,優(yōu)先考慮條件不成立可以立即跳出的情況
// 優(yōu)先考慮可以跳出的流程
if (!a) {
return;
}
if (!b) {
return;
}
if (!c) {
return;
}
// 不建議使用嵌套的方式
if (a) {
if (b) {
if (c) {
}
}
}
- 三目運(yùn)算只有在能增加代碼清晰度和整潔度的時(shí)候才推薦使用
// 正確
NSInteger value = 5;
result = (value != 0) ? x : y;
// 錯(cuò)誤
result = a > b ? x = c > d ? c : d : y;
switch case 語句
-
switch case如果判斷條件是枚舉類型的值,則case也應(yīng)該為枚舉值,而不是 case 1;并且可以省略default處理
RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;
switch (menuType) {
case RWTLeftMenuTopItemMain:
// ...
break;
case RWTLeftMenuTopItemShows:
// ...
break;
case RWTLeftMenuTopItemSchedule:
// ...
break;
}
其他規(guī)則
初始化方法(構(gòu)造方法)規(guī)則
- 初始化方法的返回類型用
instancetype,而不是用id; - 如果重寫init方法,必須調(diào)用[super init]方法;
直接放回對(duì)應(yīng)數(shù)據(jù)時(shí),無需添加get, calc單詞
- (CGFloat)cellHeight; // 正確
- (CGFloat)getCellHeight; // 錯(cuò)誤
- (CGFloat)calcCellHeight; // 錯(cuò)誤
單例的聲明和使用規(guī)則
- 獲取單例的類方法
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- UIApplication的使用規(guī)則
-
獲取單例的方法不使用點(diǎn)語法;
-
// 正確
[UIApplication sharedApplication].delegate;
// 錯(cuò)誤
UIApplication.sharedApplication.delegate;
設(shè)置常用屬性,直接使用點(diǎn)語法
// 正確
self.view.backgroundColor = [UIColor redColor];
// 錯(cuò)誤
[self.view setBackgroundColor:[UIColor redColor]];
協(xié)議和代理方法命名規(guī)則
- 協(xié)議方法名開頭應(yīng)與類名一樣(
不包含前綴) - 必須將本身作為參數(shù)傳遞給外部,如
(UITableView *)tableView - 傳遞所需參數(shù)給外部,如
(NSIndexPath *)indexPath;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath;
可以使用({})語法模塊化設(shè)置控件屬性(可選)
- 要使用
({})語法,前提: 該屬性需由strong修飾;
// 前提: 該屬性需由strong修飾
/** 名稱Label */
@property (nonatomic, strong) UILabel *nameLabel;
self.nameLabel = ({
UILabel *label = [[UILabel alloc] init];
label.textAlignment = NSTextAlignmentCenter;
label.font = [UIFont systemFontOfSize:12];
label.textColor = [UIColor orangeColor];
label;
});