第二版 2017.1.6
第三版 2018.8.29
以下規(guī)范基于 Coding Guidelines for Cocoa - Apple & 唐巧的博客
留白
- 使用tab而不是空格
- 使用留白劃分程序邏輯塊
- 方法名過(guò)長(zhǎng)時(shí)注意換行:盡量讓代碼保持在120列之內(nèi); 通過(guò)設(shè)置 Xcode > Preferences > Text Editing > Show page guide,來(lái)使越界更容易被發(fā)現(xiàn)
應(yīng)該
// blocks are easily readable
[UIView animateWithDuration:1.0 animations:^{
// something
} completion:^(BOOL finished) {
// something
}];
不應(yīng)該
// colon-aligning makes the block indentation hard to read
[UIView animateWithDuration:1.0
animations:^{
// something
}
completion:^(BOOL finished) {
// something
}];
不應(yīng)該
if([MePagePushManager shareInstance].isPushRegiste
&&[MePagePushManager shareInstance].isPushAvailable){
[[MePagePushManager shareInstance] updatePushToken];
}
命名
目的:簡(jiǎn)潔易懂
- 使用駝峰格式命名,請(qǐng)不要使用駝峰和XXX_XXX混用,統(tǒng)一命名格式;
- 文件命名前綴:
- 當(dāng)與系統(tǒng)和第三方命名沖突時(shí)添加前綴以示區(qū)分;如:WBImageView
- 當(dāng)被提供為SDK時(shí),使用前綴,以示歸屬;如:SiaoFloatView;在項(xiàng)目中可用Bridge或者子類方式轉(zhuǎn)換文件名,如:上述的SiaoFloatView,可用Bridge.h宏文件替換命名,或者 子類化出FloatView;
- 文件、類、變量命名應(yīng)使用英文名稱
- 不使用拼音;
- 不使用毫無(wú)意義的簡(jiǎn)短的英文字母,名稱應(yīng)盡量描述出含義;
- 請(qǐng)勿使用個(gè)人名字相關(guān)的命名;第三方框架除外.
- 命名單詞盡量寫全;如:button,view.
- 類名首字母大寫,方法首字母小寫,方法中的參數(shù)首字母小寫,同時(shí)盡量讓方法的命名讀起來(lái)像一句話,能夠傳達(dá)出方法的意思;
- 以上命名,做到顧名思義,這樣的方法名和變量名無(wú)需添加注釋。
- 方法命名應(yīng)該和apple官方命名一致
應(yīng)該
- (UIView *)viewWithModel:(Model *)model other:(id)other {
NSInteger i = height + 5 * 2;//應(yīng)該用空格隔開
}
不應(yīng)該
-(UIView*)viewWithModel:(Model *)model other:(id)other {
NSInteger i = height+5*2;
}
應(yīng)該
UIButton *settingsButton;
CGFloat view_height;//VIEW_HEIGHT;
不應(yīng)該
UIButton *setBut;
不應(yīng)該
CGFloat View_Height;
- 點(diǎn)擊事件命名didClickSendButton; didTapBoxView; 等
- 定義網(wǎng)絡(luò)請(qǐng)求,requestXXX,解析網(wǎng)絡(luò)請(qǐng)求dealWithXXXX;
- (void)requestHotlistData:(BOOL)loadMore completion:(void(^)(BOOL success))completion;
- (void)dealWithHotListResponseWithData:(NSArray *)data;
文檔和代碼組織
- 數(shù)字注釋出含義
- 參數(shù)不允許為nil時(shí),加注釋
- 使用Use #pragma mark對(duì)功能實(shí)現(xiàn)和協(xié)議實(shí)現(xiàn)分組,如下示例
- 清除警告,個(gè)人的提醒用//@todo yourname or //@waring yourname代替,這樣只需要在項(xiàng)目里搜索 @todo yourname就可以找到,團(tuán)隊(duì)協(xié)作 在不影響他人的情況下協(xié)同開發(fā);需要提醒大家的才用#warning ,保持項(xiàng)目的整潔,及時(shí)清除警告。
- 文件自上到下從優(yōu)先級(jí)以及包含與被包含的關(guān)系排列,禁止無(wú)須排列;
應(yīng)該:

不應(yīng)該

if (model.more_info.text.length > 0) {
height += comments.count > 0 ? 6.0 : 0.0;//有回復(fù)且有moreInfo 有一個(gè)上邊距 6
height += [self moreInfoHeight];
} else {
height -= 1;//由于為了適配有表情的行高(+2) 這里減去2
}
#pragma mark Properties
@dynamic someProperty;
- (void)setCustomProperty:(id)value {}
#pragma mark Lifecycle
+ (id)objectWithThing:(id)thing {}
- (instancetype)init {}
//@todo xiaodong
//@warning xiaodong
聲明
- 如果屬性的可修改性是一個(gè)實(shí)現(xiàn)細(xì)節(jié),聲明不可修改屬性。這是一個(gè)合理的顯式的聲明實(shí)例變量的原因
- 如果一個(gè)屬性只在init內(nèi)被賦值了一次,那么就聲明為readonly
- 如果返回可修改對(duì)象,并且實(shí)現(xiàn)中不修改該對(duì)象,則屬性聲明為copy, strong應(yīng)該僅用于暴露一個(gè)可修改對(duì)象
- 使用[setupProperty]給需要初始化的變量初始化;
- _property&self.property
- 懶加載的使用, 需要使用懶加載才使用,不應(yīng)該為了代碼簡(jiǎn)潔而使用.
大家知道,self.property 其實(shí)是調(diào)用了類的 [self property] 方法,所以這其實(shí)是有一層方法調(diào)用的隱藏,很多時(shí)候,我們需要延遲初使化一個(gè)類成員的時(shí)候,就會(huì)把這個(gè)成員的初使化方法寫在這個(gè) [self property] 方法的實(shí)現(xiàn)中。
那么問(wèn)題來(lái)了,當(dāng)你在閱讀別人代碼時(shí),看到 self.property 的時(shí)候,你會(huì)想:這里會(huì)不會(huì)有一些隱藏的函數(shù)實(shí)現(xiàn)?于是你需要跳轉(zhuǎn)到其方法實(shí)現(xiàn)中去查找。但是在實(shí)際開發(fā)中,大部分的 property 其實(shí)是使用編譯器自動(dòng)生成的 Getter 和 Setter 方法,于是你會(huì)找不到實(shí)現(xiàn),這個(gè)時(shí)候,你才知道:“哦,原來(lái)這段代碼并沒有做自定義的成員初使化工作”。
這種默認(rèn)的隱藏在代碼中多了,會(huì)影響代碼的閱讀和維護(hù)。其實(shí)大部分的類成員變量都需要在類初使化方法中賦值,大部分的 UIViewController 的成員變量,都需要在 viewDidLoad 方法中賦值。那既然這樣,不如直接在相應(yīng)的方法中用一個(gè)名為 setupProperty 方法直接進(jìn)行初使化。這樣的好處是,代碼的可讀性更好了,self.property 只有需要延遲初使化的情況下才被使用。
@property (attributes) id<Protocol> object;
@property (nonatomic, strong) NSObject<Protocol> *object;
@implementation WKHotSearchViewController{
NSObject *_object;
}
- Constructors 應(yīng)該返回instancetype而不是id
不應(yīng)該
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.container addSubview:self.userIconImg];
[self.container addSubview:self.nickNameLab];
[self.container addSubview:self.timeLab];
[self.container addSubview:self.contentImg];
[self.container addSubview:self.contentLab];
[self.container addSubview:self.bottomLine];
[self.contentView addSubview:self.container];
[self addMasonry];
}
return self;
}
常量
- 常量是容易重復(fù)被使用和無(wú)需通過(guò)查找和代替就能快速修改值。
- 常量應(yīng)該使用static來(lái)聲明而不是使用#define,除非顯式地使用宏。
static NSString * const IDENTIFY = @"weibo.com";
static CGFloat const CELL_HEIGHT = 50.0;
枚舉
- 按照系統(tǒng)的命名方式
應(yīng)該:
typedef NS_ENUM(NSInteger, PushType) {
PushTypeUnknown,
PushTypeArticleRemotePush, // 遠(yuǎn)程Push打開正文
PushTypeArticleLocalPush, // 本地Push打開正文
};
注釋
- 應(yīng)做到盡量代碼“自解釋”,而不需要再編寫注釋。
- 當(dāng)需要注釋時(shí),注釋應(yīng)該用來(lái)解釋這段特殊代碼為什么要這樣做。
- 不注釋代碼塊,無(wú)用的代碼直接刪除
- 不要一行一個(gè)注釋,單行注釋,可加在代碼末尾;
- 注釋不是分割行的標(biāo)記!
應(yīng)該
@property (nonatomic,strong) UILabel *nickNameLab; // 昵稱 + 贊了你的評(píng)論
不應(yīng)該
/* headerTop */
WBHeadlineHeaderView *articleHeaderView;
/* headerMiddle */
UIView *headerMiddleView;
WBArticleMedileHeaderView *continuseView;
- 不清楚MiddleView是什么作用,加了無(wú)意義,等于沒加;
控制結(jié)構(gòu)
- 除if (xxx) return; 的用法,if必須加括號(hào)
- 所有左括號(hào)應(yīng)該和關(guān)聯(lián)的表達(dá)式同一行,結(jié)尾換行
- 關(guān)鍵詞和括號(hào)間加空格
- 括號(hào)和它包括的內(nèi)容間不加空格
if (somethingIsBad) return;
if (something == nil) {
// do stuff
} else {
// do other stuff
}
- 黃金路徑,早返回
應(yīng)該
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
//Do something important
}
不應(yīng)該
- (void)someMethod {
if ([someOther boolValue]) {
//Do something important
}
}
異常和錯(cuò)誤處理
- 不用使用異常做控制流
- 表示錯(cuò)誤,使用NSError **參數(shù),或者給ReactiveCocoa signal發(fā)一個(gè)錯(cuò)誤
- 方法參數(shù)不匹配時(shí),使用NSAssert
- 循環(huán)引用
對(duì)象語(yǔ)法
- 數(shù)組和字典對(duì)象文法內(nèi)容兩邊使用一個(gè)空格
- 字典文法中,鍵值和冒號(hào)之間不留空格,值和冒號(hào)之間使用一個(gè)空格
- 數(shù)組和字典setObject時(shí)候需要保證object非nil
NSArray *theStuff = @[@1, @2, @3];
NSDictionary *keyedStuff = @{GHDidCreateStyleGuide: @YES};
- 更長(zhǎng)或者更復(fù)雜的文法應(yīng)該分成多行(可選以逗號(hào)結(jié)尾)
NSArray *theStuff = @[
@"Got some long string objects in here.",
[AndSomeModelObjects too],
@"Moar strings."
];
NSDictionary *keyedStuff = @{
@"this.key": @"corresponds to this value",
@"otherKey": @"remoteData.payload",
@"some": @"more",
@"JSON": @"keys",
@"and": @"stuff",
};
類的結(jié)構(gòu)
graph TD
A[LifeCycle] -->B(Request)
B -->C(UITableViewDelegate&DataSource)
C -->D(...)
D -->E(Setter&Getter)
- 注意Lazy init的用法
- 精簡(jiǎn)VC
第三方庫(kù)引入
- 引入第三方庫(kù)前,需要充分調(diào)研該庫(kù)的質(zhì)量,引入github的庫(kù),原則上應(yīng)該star在500+以上才考慮
- 引入的庫(kù)若支持Pods管理,以Pods來(lái)組織代碼
- 引入的庫(kù)應(yīng)該保持代碼不變,在庫(kù)的上層進(jìn)行自定義,而不應(yīng)該修改庫(kù)本身
引用文件
- 在.h中盡量使用@class(如果不需要知道引用文件的具體屬性),在.m文件中使用#import
擴(kuò)展
- 擴(kuò)展應(yīng)該按照所提供的功能命名,不要?jiǎng)?chuàng)建保護(hù)傘拓展
- 擴(kuò)展方法一定要加前綴
- 如果你想給子類或者單元測(cè)試暴露私有方法,使用 Class+Private命名創(chuàng)建一個(gè)分類
- (void)pf_viewDidLoad
{
[self pf_viewDidLoad];
}
兼容64位
- 整型的聲明使用NSInteger
- 浮點(diǎn)使用CGFloat(UI相關(guān))和double
- 字符串占位符不再使用%d, %ld, 將對(duì)應(yīng)的整型使用對(duì)象文法包裝,并使用%@的打印 [NSString stringWithFormat:@"%@", @(intVar)]
- 枚舉定義采用新風(fēng)格(NS_ENUM),并指定類型為NSInteger