@(〓〓 iOS-實用技術)[Objective-C編碼規(guī)范]
- 作者: Liwx
- 郵箱: 1032282633@qq.com
目錄
- 11.Objective-C 編碼規(guī)范指南
- 項目工程結構
- 代碼結構
- 項目Xode相關配置
- 代碼縮進配置
- 代碼行號顯示配置
- 項目編碼建議
- 注釋
- 方法注釋
- 屬性注釋
- 命名與編碼規(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ī)則
- 初始化方法(構造方法)規(guī)則
- 直接放回對應數(shù)據(jù)時,無需添加get, calc單詞
- 單例的聲明和使用規(guī)則
- 設置常用屬性,直接使用點語法
- 協(xié)議和代理方法命名規(guī)則
- 可以使用({})語法模塊化設置控件屬性(可選)
項目工程結構
代碼結構
-
實現(xiàn)文件中的代碼結構兼丰,提倡以下約定:
- 用
#pragma mark -
將函數(shù)或方法按功能
進行分組;分組之間空2行,方法之間空1行
. - delgate或協(xié)議相關方法放到一般內(nèi)容之后房铭。
- 用
#pragma mark - Lifecycle (生命周期)
- (void)dealloc {}
- (instancetype)init {}
- (void)viewDidLoad {}
- (void)viewWillAppear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}
#pragma mark - Private (私有方法,比如初始控件設置方法/類內(nèi)部業(yè)務處理方法)
- (CGFloat)setupTableView {}
#pragma mark - Public (對外公開方法)
- (CGFloat)reloadAllData {}
#pragma mark - Network (加載網(wǎng)絡數(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響應函數(shù),如按鈕點擊事件)
- (void)saveButtonClick:(UIButton *)saveButton {}
#pragma mark - KVO/Notification (KVO/通知響應函數(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 {}
項目Xode相關配置
代碼縮進配置
- 只用
空格縮進
,1個TAB = 4個空格字符
在
XCode->Preferences->Text Editing->Indentation
中進行如下設置:
ps. 設置成4個,是因為Xcode的默認縮進是4個空格。
代碼行號顯示配置
- Xcode行號顯示配置
勾選上
XCode->Preferences->Text Editing->Editing中的Line numbers
收擦,開啟行號提示髓霞。
項目編碼建議
-
建議:每行代碼的長度最多
不超過120個字符
;
勾選
XCode->Preferences->Text Editing->Editing
晶默,并將長度設置成120個字符來打開行寬指示。設置成功時Xcode會出現(xiàn)一條豎線! (可選配置
)
建議:為了簡潔和便于閱讀,請嘗試將單個函數(shù)或方法的實現(xiàn)代碼控制在
50行
內(nèi);單個實現(xiàn)文件里的代碼行數(shù)控制在500~600行
內(nèi);建議:為了簡潔和便于閱讀,請嘗試將單個函數(shù)或方法的實現(xiàn)代碼控制在
50行
內(nèi);當接近或超過800行時为流,就應當開始考慮分割實現(xiàn)文件了呕屎。
注釋
方法注釋
- 方法注釋規(guī)則
- 注釋應該盡量保持
簡潔
,代碼應該盡量達到能自我解釋的程度; - 注釋必須和代碼保持同步敬察。不要出現(xiàn)代碼修改了秀睛,注釋不更新的情況;
-
對外公開方法 使用
/** 方法描述 */
或VVDocumenter-Xcode
注釋,公開方法需盡量注釋清楚;如果公開方法有帶參數(shù)
,可按需對參數(shù)進行說明; -
其他方法可使用
// 方法描述
或VVDocumenter-Xcode
注釋; - 方法內(nèi)部
行注釋需對齊
;
- 注釋應該盡量保持
// .m文件
// 按鈕點擊事件處理 (非對外公開的方法)
- (void)saveButtonClick:(UIButton *)saveButton {
NSLog(@"Hello Liwx"); // 打印日志
NSLog(@"Hello Liwx"); // 打印日志
}
/** 刷新全部數(shù)據(jù) (對外公開的方法)*/
- (void)reloadAllData {
//Do Something
}
// .h文件
/** 刷新全部數(shù)據(jù) (對外公開的方法)*/
- (void)reloadAllData;
屬性注釋
建議
外部屬性
注釋建議使用/** 屬性描述 */
注釋, 因為Xcode對此類注釋有提示功能,便于開發(fā)人員在編碼時能更快的了解該屬性的作用;建議屬性與
@interface ... @end
之間各空1行
;
@interface ViewController ()
/** 時間 */
@property (nonatomic, copy) NSString *time;
@end
- 私有屬性注釋可使用
// 屬性描述
或/** 屬性描述 */
注釋;
命名與編碼規(guī)范
類名命名規(guī)則
- 類名命名規(guī)則
類名、類別名字及協(xié)議名字莲祸,都采用
大駝峰式命名規(guī)則
-
文件名要能反映出它所包含的類的名稱
如:NSString.h 和 NSString.m 包含了NSString類的定義和實現(xiàn)
-
Category的文件名要包含它所擴展的那個類的名稱蹂安,并且類別名稱要盡量能夠描述它的功能
UIImage+Resize.h 或 UIImage+TintColor.h
-
在面向特定應用的代碼中,類名盡量避免使用前綴锐帜,每個類都使用相同的前綴會影響可讀性
面向特定應用的代碼田盈,指那些只會在一個項目中使用的代碼,不會被用于其他項目中的代碼缴阎。
-
在面向多應用的代碼中允瞧,類名要使用前綴,防止命名沖突
面向多應用的代碼蛮拔,指那些會被多個項目共同使用的代碼述暂。
比如CRKit這個類庫中,使用了CR前綴建炫。
-
建議:前綴至少使用三個字母
此條是為了減少命名沖突畦韭。但鑒于目前流行前綴大多都是兩個字母,所以此條不做強制要求
協(xié)議編碼規(guī)則
- 協(xié)議編碼規(guī)則
- 協(xié)議聲明或定義中肛跌,類型標識符艺配、協(xié)議名稱、尖括號之間
不留空格
;
- 協(xié)議聲明或定義中肛跌,類型標識符艺配、協(xié)議名稱、尖括號之間
// 協(xié)議聲明協(xié)議名稱衍慎、尖括號之間不留空格
@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;
- 定義或聲明類時如果名稱過長采用
后綴縮寫
;注意大小寫;- 如控制器
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)動詞(
can
,should
,will
等)來提?高清晰性,但不要使?用 do 或 does;
// 正確
- (BOOL)canHide;
- (BOOL)shouldRefreshData;
- (void)willChangeData
方法聲明和編碼規(guī)范
- 方法聲明中转唉,
-/+
和返回值類型
之間要空1個空格
,方法名
和參數(shù)類型
之間以及參數(shù)類型和參數(shù)名之間不留空格
;
- (instancetype)initwithTitle:(NSString *)title; // 正確
-(instancetype)initwithTitle:(NSString *)title; // 錯誤
- (instancetype) initwithTitle:(NSString *)title; // 錯誤
- (instancetype)initwithTitle: (NSString *)title; // 錯誤
- (instancetype)initwithTitle:(NSString *) title; // 錯誤
- 方法名和參數(shù)名應該盡量讀起來像一句話西饵。
如:convertPoint:fromRect: 或者 replaceCharactersInRange:withString:
- 當各個參數(shù)是接收者的某個屬性時酝掩,方法名中不要用
"and"
來連接;
// 正確
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
// 錯誤
- (instancetype)initWithNibName:(NSString *)nibNameOrNil andBundle:(NSBundle *)nibBundleOrNil;
- 方法名之后空
1格
,緊隨左大括號{
,無需換號;但是右大括號}
必須另取一行
- (CGFloat)setupTableView {
// Do Something
}
方法調(diào)用父類方法編碼規(guī)則
- 重載父類方法時,遇到必須調(diào)用父類方法時眷柔。調(diào)用super的代碼和重載的代碼之間留一行空行期虾。將super方法的調(diào)用和重載代碼區(qū)隔開來.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// Do Something
}
函數(shù)
-
函數(shù)指純C函數(shù)原朝,這里提倡與蘋果風格類似的約定。
函數(shù)名采用大駝峰式
命名方式镶苞,``參數(shù)名采用小駝峰式`命名方式如果函數(shù)和某個特定類型相關喳坠,那么函數(shù)名前綴要和類型前綴一樣
如
CGRectMake(CGFloat x, CGFloat y, CGFloat width, CGFloat height)
等
變量
成員屬性編碼規(guī)則
-
屬性名
和變量名
都采用小駝峰式
命名規(guī)則; -
禁止使用
匈牙利標記法或含糊不清的縮寫單詞
來命名變量; - 指針符號
"*" 靠近變量名字
。(常量定義除外) - property屬性
括號兩邊各空1格
, 屬性關鍵字以逗號加空格隔開 -
除了xib拖控件
的方式除外,nonatomic需放在最前面
, strong,weak,assign,copy應放在nonatomic后面; - 成員變量和局部變量聲明Demo
// 成員變量聲明
/** 標題Label (正確) */
@property (nonatomic, copy) NSString *titleLabel;
/** 標題Label (錯誤) */
@property (nonatomic, copy) NSString *titleLbl;
/** 標題Label (錯誤) */
@property(nonatomic, copy) NSString *titleLabel;
/** 標題Label (錯誤) */
@property (nonatomic,copy) NSString *titleLabel;
/** 標題Label (錯誤) */
@property (copy, nonatomic) NSString *titleLabel;
// 局部變量聲明
NSString *titleLabel = nil; // 正確
NSString* titleLabel = nil; // 錯誤
NSString * titleLabel = nil; // 錯誤
NSString*titleLabel = nil; // 錯誤
- 使用property時茂蚓,優(yōu)先使用點語法;
/** 名稱 */
@property (nonatomic, copy) NSString *name;
// 訪問成員屬性時,優(yōu)先使用點語法
self.name = @"Liwx";
- 賦值操作符
"="
兩邊各空1格
;
self.name = @"Liwx"; // 正確
self.name= @"Liwx"; // 錯誤
self.name=@"Liwx"; // 錯誤
- 如果使用property修飾的是屬性BOOL值壕鹉,建議為getter方法加上一個
"is"開頭
的別名。
@property (assign, getter = isSelected) BOOL selected;
- 如果
網(wǎng)絡獲取的屬性
數(shù)據(jù)為數(shù)值型
的,則定義屬性也應該為數(shù)值型
;如果是枚舉類型,則應定義對應枚舉類型;不應將數(shù)值型定義為NSString字符串類型;
// 正確
@property (copy,nonatomic) UserStatus *userStatus;
// 錯誤
@property (copy,nonatomic) NSString *userStatus;
常量
Foundation框架常量賦值規(guī)則
- 為了提高代碼簡潔度,創(chuàng)建NSString, NSDictionary, NSArray, 以及NSNumber等常量時聋涨,使用Literals語法;
// 正確
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
// 錯誤
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;
- 只在某一個特定文件里面使用的常量晾浴,用
static
static關鍵字保證變量只有文件作用
域,可以避免變量名重名
造成的鏈接錯誤問題牍白。
如: static CGFloat const RWImageThumbnailHeight = 50.0;
枚舉與宏定義
枚舉編碼規(guī)則
- 定義枚舉常量時脊凰,使用
NS_ENUM
或NS_OPTIONS
;
因為NS_ENUM和NS_OPTIONS都提供了類型檢查;
- 定義枚舉時,
一定要注釋
,并且格式為:[枚舉類型名(UITableViewStyle)] + [類型(Plain)]
;
NS_ENUM定義普通枚舉
- 使用
NS_ENUM定義普通枚舉
Demo,建議
枚舉值 = 對應數(shù)值
// 應用皮膚樣式
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ī)則
- 通知常量命名格式: [相關聯(lián)的類名字] + [Did | Will] + [獨一無二的一段名稱] + Notification
-
建議:
定義通知常量不采用宏定義的方法
; - .h文件, 全局通知聲明時需加上UIKIT_EXTERN關鍵字
- .m文件, 通知常量名和值必須保持一致
-
建議:
- 通知命名Demo
// .h文件
// 全局通知聲明時需加上UIKIT_EXTERN關鍵字
UIKIT_EXTERN NSString *const UIKeyboardDidChangeFrameNotification ;
// .m文件, 通知常量名和值必須保持一致
NSString *const UIKeyboardDidChangeFrameNotification = @"UIKeyboardDidChangeFrameNotification";
異常命名規(guī)則
- 異常名字的命名規(guī)則:[前綴] + [獨一無二的一段名稱] + Exception
如:NSColorListIOException
布爾值
- Objective-C的布爾值只使用
YES
和NO
; -
注意
:true
和false
只能用于CoreFoundation,C或C++的代碼中
; - 禁止將某個值或表達式的結果與YES進行比較
// 正確
if (self.isLogin) {}
// 錯誤
if (self.isLogin == YES) {}
- 如果返回值為BOOL值,必須確保返回值為YES或NO,最好不要存在多值的情況
// 正確
- (BOOL)isLogin {
return self.userToken.length != 0 ? YES : NO;
}
// 錯誤
- (BOOL)isLogin {
return self.userToken.length;
}
條件語句
if else 條件語句
- 條件語句的語句體茂腥,即便只有一行狸涌,也
不能省略花括弧
; - 判斷條件之后空
1格
,緊隨左大括號{
,無需換號;但是右大括號}
必須另取一行; - else或 elseif應緊隨在右大括號
}
之后,并且中間空1格
;
// 正確
if (isLogin) {
// Do Something
} else {
// DO Something
}
// 錯誤
if (isLogin)
{
// Do Something
}
else {
// DO Something
}
// 正確
if (error == nil) {
return success;
}
// 錯誤
if (error == nil)
return success;
- 多層嵌套的條件語句,優(yōu)先考慮條件不成立可以立即跳出的情況
// 優(yōu)先考慮可以跳出的流程
if (!a) {
return;
}
if (!b) {
return;
}
if (!c) {
return;
}
// 不建議使用嵌套的方式
if (a) {
if (b) {
if (c) {
}
}
}
- 三目運算只有在能增加代碼清晰度和整潔度的時候才推薦使用
// 正確
NSInteger value = 5;
result = (value != 0) ? x : y;
// 錯誤
result = a > b ? x = c > d ? c : d : y;
switch case 語句
-
switch case
如果判斷條件是枚舉類型
的值,則case也應該為枚舉值,而不是 case 1;并且可以省略default
處理
RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;
switch (menuType) {
case RWTLeftMenuTopItemMain:
// ...
break;
case RWTLeftMenuTopItemShows:
// ...
break;
case RWTLeftMenuTopItemSchedule:
// ...
break;
}
其他規(guī)則
初始化方法(構造方法)規(guī)則
- 初始化方法的返回類型用
instancetype
,而不是用id
; - 如果重寫init方法,必須調(diào)用[super init]方法;
直接放回對應數(shù)據(jù)時,無需添加get
, calc
單詞
- (CGFloat)cellHeight; // 正確
- (CGFloat)getCellHeight; // 錯誤
- (CGFloat)calcCellHeight; // 錯誤
單例的聲明和使用規(guī)則
- 獲取單例的類方法
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- UIApplication的使用規(guī)則
-
獲取單例
的方法不使用點語法
;
-
// 正確
[UIApplication sharedApplication].delegate;
// 錯誤
UIApplication.sharedApplication.delegate;
設置常用屬性,直接使用點語法
// 正確
self.view.backgroundColor = [UIColor redColor];
// 錯誤
[self.view setBackgroundColor:[UIColor redColor]];
協(xié)議和代理方法命名規(guī)則
- 協(xié)議方法名開頭應與類名一樣(
不包含前綴
) - 必須將本身作為參數(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;
可以使用({})
語法模塊化設置控件屬性(可選)
- 要使用
({})
語法,前提: 該屬性需由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;
});