
前言
關(guān)于代碼規(guī)范的重要性這里不做過(guò)多解釋,能看到這篇文章說(shuō)明你已經(jīng)開(kāi)始重視代碼規(guī)范了(代碼規(guī)范看起來(lái)是在限制你的自由和發(fā)揮,其實(shí)它是在間接的幫助你變得更優(yōu)秀。)。
適當(dāng)?shù)拇a規(guī)范和標(biāo)準(zhǔn)絕不是消滅代碼內(nèi)容的創(chuàng)造性、優(yōu)雅性,而是限制過(guò)度個(gè)性化,以一種普遍認(rèn)可的統(tǒng)一方式一起做事,進(jìn)而提高工作效率,降低溝通成本。代碼的字里行間流淌著的是軟件和程序員的血液,質(zhì)量的提升是盡可能少踩坑、杜絕踩重復(fù)的坑,切實(shí)提升系統(tǒng)穩(wěn)定性,碼出質(zhì)量
根據(jù)約束力度,暫時(shí)把規(guī)范約定為2個(gè)等級(jí),分別是 必須 和 建議。
(一)命名規(guī)范
1. 通用命名規(guī)范
Tips:
所有的命名都應(yīng)該遵循3個(gè)基本原則,即“清晰性”、“一致性”、“不要自我指涉”。
- [必須] 清晰性:好的命名應(yīng)該是能自我描述的。
正例:
removeObject:、[string stringByReplacingOccurrencesOfString:@"1" withString:@"2"]
反例:
remove:(不清楚,要?jiǎng)h除什么?)、string.replace("1", "2")
(是將"1"替換成"2"還是將"2"替換成"1"?是將第1個(gè)"1"替換成"2"還是將所有的"1"都替換成"2")
-
[必須] 一致性:命名應(yīng)該和上下文乃至全局保持一致性,相同類型或者具有相同作用的變量的命名方式應(yīng)該相同或類似。
正例: NSDictionary、NSArray、NSSet這幾個(gè)集合類都是用count來(lái)表示數(shù)量而不是一個(gè)用count其它的用amount或其他單詞,這體現(xiàn)了命名的一致性。 @property (readonly) NSUInteger count; -
[必須] 禁止自我指涉:命名不要自我指涉。通知、掩碼常量等除外(通常指那些可以進(jìn)行按位運(yùn)算的枚舉值)。
正例: NSString 反例: NSStringObject [必須] 杜絕過(guò)度縮寫(xiě),嚴(yán)禁自創(chuàng)縮寫(xiě)(例如把
button縮寫(xiě)為btn);國(guó)際通用縮寫(xiě)名稱除外(例如ATM、GPS)。
Tips:
你明白這個(gè)縮寫(xiě)的意思不代表其他人也一定會(huì)明白,你的代碼可能會(huì)被任何人閱讀,而閱讀的人來(lái)自不同的地方接受不同的教育不同的文化,所有建議一般不要亂使用縮寫(xiě),只使用那些國(guó)際通用縮寫(xiě)。如果為了縮寫(xiě)創(chuàng)建一個(gè)縮寫(xiě)對(duì)照表只會(huì)增加代碼閱讀復(fù)雜度。
正例:
destinationSelection、setBackgroundColor
反例:
destSel、setBgColor
-
[必須] 杜絕無(wú)意義的拼音,國(guó)際通用名稱或者地名人名除外(例如
alibaba、taobao、hangzhou)。反例: DaZhePromotion(打折) -
[必須] 命名要盡可能的清晰并簡(jiǎn)潔,如果兩者不能兼得,則以清晰為主。
正例: insertObject:atIndex: 反例: insert:at:(不清晰,插入什么?at代表什么?) -
[必須] 代碼和注釋中都要避免使用任何語(yǔ)言的種族歧視性詞語(yǔ)。
正例: secondary 反例: slave -
[必須] 類名、協(xié)議名、函數(shù)名、常量名、枚舉名等一些全局命名需要添加前綴,前綴需要大于2個(gè)字符且全部大寫(xiě)。
Tips: 系統(tǒng)保留任意兩個(gè)字符作為前綴的使用權(quán), 包括但不限于NS、UI、CG、CF、CA、WK、MK、CI、NC;前綴若等于2個(gè)字符可以考慮添加_。 正例: ZT_LoginViewController 反例: ZTLoginViewController [必須] 類名、協(xié)議名、函數(shù)名、常量名、枚舉名等一些全局命名遵循首字母大寫(xiě)的駝峰命名方式,首個(gè)單詞是
HTTP這種特殊詞除外。[必須] 方法名、屬性名等一些非全局命名遵循首字母小寫(xiě)的駝峰命名方式命名,首個(gè)單詞是
HTTP這種特殊詞除外。-
[必須] 成員變量需要以_開(kāi)頭。
正例: NSString *_nameString; -
[建議] 在給常量或變量命名時(shí),盡量將表示類型的名詞放在詞尾,以提升辨識(shí)度。
正例: nameLabel、nameString 反例: name(name是字符串還是什么?) -
[建議] 如果模塊、接口、類、方法使用了模式,在命名時(shí)盡量體現(xiàn)出具體模式。
正例: OrderFactory、LoginProxy -
[建議] 局部臨時(shí)變量命名可以加上標(biāo)識(shí)符作為前綴。
正例: t_label、t_string(t在這里表示temp)
2. 類命名規(guī)范
首先作為一個(gè)開(kāi)發(fā)者,有一個(gè)學(xué)習(xí)的氛圍跟一個(gè)交流圈子特別重要,這是一個(gè)我的iOS開(kāi)發(fā)交流群:130 595 548,不管你是小白還是大牛都?xì)g迎入駐 ,讓我們一起進(jìn)步,共同發(fā)展?。ㄈ簝?nèi)會(huì)免費(fèi)提供一些群主收藏的免費(fèi)學(xué)習(xí)書(shū)籍資料以及整理好的幾百道面試題和答案文檔?。?/strong>
-
[必須] 類名命名風(fēng)格由"前綴+類的名稱+類的類型"3個(gè)部分組成,前綴必須大于2個(gè)字符且全部大寫(xiě)(如果等于2個(gè)字符可以添加_);類的名稱遵循首字母大寫(xiě)駝峰式命名,類的名稱要能表達(dá)出該類的功能;類的類型必須使用全稱,嚴(yán)禁使用縮寫(xiě)(例如
vc代替viewController,cell代替TableViewCell),命名方式和名稱命名一樣首字母大寫(xiě)。
正例:
WXYZ_LoginViewControler WXYZ_表示前綴,Login表示該類跟登錄相關(guān),ViewController表示該類是一個(gè)視圖控制器而不是View。
3. 方法命名規(guī)范
[必須] 所有方法名稱禁止以new開(kāi)始。
-
[必須] 所有方法名稱禁止使用_開(kāi)始。
Tips: 系統(tǒng)會(huì)使用_開(kāi)頭命名一些系統(tǒng)私有方法 -
[必須] 內(nèi)部私有方法需要增加前綴,前綴需要保持唯一性(例如p_)。
Tips: 給私有方法加前綴有2個(gè)好處: 1. 增加辨識(shí)度,提高代碼可讀性。 2. 避免自己的方法無(wú)意間覆蓋了系統(tǒng)/框架同名的私有方法。 -
[必須] 如果方法返回接收者的某個(gè)屬性值,那么請(qǐng)直接使用屬性名作為方法名。
正例: - (CGSize)cellSize; 反例: - (CGSize)getCellSize; -
[必須] 如果方法間接返回一個(gè)或多個(gè)值,那么請(qǐng)用"
getXXX"命名,這種命名只適用于返回多個(gè)數(shù)據(jù)項(xiàng)的情況。正例: - (void)getCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask completionHandler:(void (^) (NSCachedURLResponse * __nullable cachedResponse))completionHandler; -
[必須] 方法的每個(gè)參數(shù)前都必須添加關(guān)鍵字。
正例: - (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag; 反例: - (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; -
[建議] 參數(shù)之前的單詞盡量能描述參數(shù)的意義。
正例: - (id)viewWithTag:(NSInteger)aTag; 反例: - (id)taggedView:(int)aTag; -
[建議] 請(qǐng)不要使用“
and”連接接收者屬性,盡管and讀起來(lái)還算順口,但隨著你創(chuàng)建的方法參數(shù)的增加,這將會(huì)帶來(lái)一系列的問(wèn)題。正例: - (int)runModalForDirectory:(NSString *)path file:(NSString *) name types:(NSArray *)fileTypes; 反例: - (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; -
[建議] 如果方法描述了兩個(gè)獨(dú)立的動(dòng)作,則可以使用"and"連接起來(lái)。
正例: - (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
4. Protocol命名規(guī)范
-
[必須]
Protocol中的方法命名以觸發(fā)消息的對(duì)象名開(kāi)頭,省略類名前綴并首字母小寫(xiě),如果它沒(méi)有關(guān)聯(lián)任何類則可以忽略這個(gè)規(guī)則。正例: - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename; -
[必須] 除非
Protocol方法只有一個(gè)參數(shù),否則冒號(hào)需緊跟在類名后面。正例: - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
5. Category命名規(guī)范
-
[必須] 分類命名也要和類命名一樣添加前綴。
正例: UIView (YYAdd) 反例: UIView (Add) [必須] 分類中聲明的方法名都要加上前綴。
-
[建議]
Category中盡量不要聲明屬性,能挪盡量挪到主類中聲明。Tips: 盡管從技術(shù)上來(lái)講可以在分類中聲明屬性,但是這么做需要格外小心, 因?yàn)樗苋菀壮霈F(xiàn)內(nèi)存上或其他一些問(wèn)題,而且一旦出現(xiàn)問(wèn)題很難排查。 [建議] 如果一個(gè)類比較復(fù)雜,那么建議使用分類組織代碼(可以參考系統(tǒng)的
UIView)。
6. Notification命名規(guī)范
-
[必須]
Notification的命名風(fēng)格由"類名前綴" + "通知事件名稱" + "Notification"3個(gè)部分組成。正例: UIApplicationDidBecomeActiveNotification UIApplication表示該通知屬于誰(shuí),DidBecomeActive表示該通知的作用,Notification表示它是一個(gè)通知。 -
[建議] 如果一個(gè)類聲明了
delegate屬性,通常情況下,這個(gè)類的delegate對(duì)象應(yīng)該可以通過(guò)實(shí)現(xiàn)的delegate方法收到大部分通知消息。Tips: 例如applicationDidBecomeActive:代理方法和NSApplicationDidBecomeActiveNotification通知(這其實(shí)也符合命名規(guī)范的基本原則"一致性")。
7. 常量命名規(guī)范
-
[必須] 如果常量局限于某"編譯單元"之內(nèi),通常在前面加小寫(xiě)字母k作為前綴,若常量在全局可見(jiàn),通常以類名作為前綴,然后采用首字母大寫(xiě)的駝峰式命令風(fēng)格。
正例: // 局部可見(jiàn) const CGFloat kAnimationDuration = 2.0; // 全局可見(jiàn) const CGFloat UIActivityIndicatorViewAnimationDuration = 2.0;
8. Exception命名規(guī)范
-
[必須] 命令規(guī)范和
Notification一樣,把后綴改為"Exception"即可。
9. 文件命名規(guī)范
[必須] 文件名全部小寫(xiě)。
[必須] 采用_連接單詞。
-
[必須] 命名的風(fēng)格:"模塊_屬性描述",可根據(jù)項(xiàng)目適當(dāng)增加描述。
正例: public_back@2x.png
(二)編碼規(guī)范
1. 通用編碼規(guī)范
[必須] 如果有使用到
CF(Core Foundation)等框架時(shí),或者是在iOS10以下系統(tǒng)使用通知和KVO時(shí),切記在dealloc方法中釋放對(duì)象以及移除通知和監(jiān)聽(tīng)。-
[必須] 在
dealloc方法內(nèi)禁止將self傳遞出去,如果self被retain,到下個(gè)runloop周期再釋放則會(huì)多次釋放導(dǎo)致crash。反例: - (void)dealloc { [self unsafeMethod:self]; } -
[必須] 禁止使用過(guò)時(shí)的方法或類,應(yīng)該及時(shí)去了解和使用新方法或類。
Tips: 對(duì)于過(guò)時(shí)的方法或類,大都是因?yàn)槠渥陨碛幸恍┤毕莼駼UG才會(huì)不建議使用,使用新方法時(shí)建議了解一下為什么廢棄掉舊方法/類。 [必須] 對(duì)剪切板的讀取操作必須放在子線程中進(jìn)行,因?yàn)橛脩艨赡茉?code>Mac上復(fù)制大量數(shù)據(jù)然后通過(guò)
iCloud同步到iPhone上。[必須]
if、else、for、while、case等后面必須要有{},除非后面是簡(jiǎn)單的return類型語(yǔ)句,例如if (xxx) return;。-
[必須] 當(dāng)方法可能會(huì)提前
return時(shí),需要要注意對(duì)象的釋放問(wèn)題,避免內(nèi)存泄漏。反例: CFArrayRef arrayRef = (__bridge CFArrayRef)array; if (x == YES) return; CFRelease(arrayRef); 以上代碼如果x等于YES的話那么arrayRef對(duì)象就會(huì)內(nèi)存泄漏。 -
[必須] 當(dāng)使用
@try處理異常時(shí),需要要注意對(duì)象的釋放問(wèn)題,避免內(nèi)存泄漏。反例: @try { CFArrayRef arrayRef = (__bridge CFArrayRef)array; do some thing…… CFRelease(arrayRef); } @catch (NSException *exception) { } 以上代碼如果do some thing……出現(xiàn)異常的話那么arrayRef就會(huì)出現(xiàn)內(nèi)存泄漏。 -
[必須] 聲明常量請(qǐng)使用
const類型聲明,禁止使用#define宏定義。Tips: 宏定義聲明常量的缺點(diǎn): 1. 宏定義只是簡(jiǎn)單的替換,缺少編譯檢查,運(yùn)行期可能會(huì)出現(xiàn)溢出或數(shù)據(jù)錯(cuò)誤等問(wèn)題。 2. 宏定義缺少類型,不方便編寫(xiě)文檔用例。 3. 宏定義可能會(huì)被不小心替換。 4. 宏定義無(wú)法編寫(xiě)注釋。 反例: #define kTime @"10" if (1 == 2) { #define kTime @"20" } NSLog(@"time = %@", kTime); 以上代碼中的if永遠(yuǎn)不會(huì)執(zhí)行,但是編譯器也會(huì)將kTime替換為@"20" -
[建議] 寫(xiě)一些公共方法時(shí),請(qǐng)盡量使用內(nèi)聯(lián)函數(shù)或者全局函數(shù)而不是宏定義。
Tips: 函數(shù)不通過(guò)對(duì)象調(diào)用,所以不會(huì)走OC的消息轉(zhuǎn)發(fā)流程,效率遠(yuǎn)高于方法調(diào)用;而且函數(shù)會(huì)有返回值和參數(shù)類型以及參數(shù)檢查,而這些都是宏定義沒(méi)有的。 正例: UIKIT_STATIC_INLINE NSString * kNSStringFromInteger(NSInteger a) { return [NSString stringWithFormat:@"%zd", a]; } 反例: #define kNSStringFromInteger(a) [NSString stringWithFormat:@"%zd", a] [必須]
UITableView使用self-sizing實(shí)現(xiàn)不等高cell時(shí),請(qǐng)?jiān)?code>tableView:cellForRowAtIndexPath:代理方法中給cell設(shè)置數(shù)據(jù)而不是tableView:willDisplayCell:forRowAtIndexPath:代理方法中設(shè)置數(shù)據(jù)。-
[必須] 只在必要的時(shí)刻使用懶加載。
Tips: 只在以下三種情況下才能使用懶加載: 1. 對(duì)象的創(chuàng)建需要依賴其他對(duì)象 2. 對(duì)象可能被使用,也可能不被使用 3. 對(duì)象創(chuàng)建比較消耗性能 [建議] 懶加載方法內(nèi)應(yīng)該只執(zhí)行需要初始化的操作,不應(yīng)該有其他不必要的邏輯代碼。
-
[必須] 使用一目運(yùn)算符時(shí)左右兩邊不能有空格。
正例: i++、++i、 反例: i ++、++ i -
[必須] 使用二目、三目運(yùn)算符時(shí)左右兩邊必須有且僅有一個(gè)空格。
正例: 1 + 2 反例: 1+2 [必須] 采用4個(gè)空格縮進(jìn),如果要使用Tab字符,請(qǐng)將1個(gè)Tab設(shè)置成4個(gè)空格。
[必須] 使用
NSUserDefaults存儲(chǔ)數(shù)據(jù)時(shí)禁止調(diào)用synchronize方法,因?yàn)橄到y(tǒng)會(huì)在合適的時(shí)機(jī)將數(shù)據(jù)保存到本地(即使程序閃退等極端情況)。-
[必須] 添加到集合中的對(duì)象應(yīng)該是不可變的,或者在加入之后其哈希碼是不可變的。
反例: NSMutableSet *sets = [NSMutableSet set]; NSMutableString *string1 = [NSMutableString stringWithString:@"1"]; [sets addObject:string1]; [sets addObject:@"12"]; [string1 appendString:@"2"]; 當(dāng) [string1 appendString:@"2"] 執(zhí)行完以后sets對(duì)象內(nèi)會(huì)包含2個(gè)@"12"。 -
[必須] 必須使用
CGRectGet獲取Frame的各種值,而不是通過(guò)frame.的方式獲取。Tips: CGRect t_frame = CGRectMake(-10, -10, -10, -10); 當(dāng)一個(gè)view的frame設(shè)置成t_frame后,其坐標(biāo)會(huì)隱式的轉(zhuǎn)換為CGRectMake(-20, -20, 10, 10),因?yàn)閷捀卟豢赡艹霈F(xiàn)負(fù)值;這時(shí)通過(guò)t_frame.的方式獲取的值都是錯(cuò)誤的,而CGRectGet會(huì)自動(dòng)幫您處理這些隱式轉(zhuǎn)換。 正例: CGRectGetWidth(frame)、CGRectGetMinX(frame)、CGRectGetMaxX(frame) 反例: frame.size.width、frame.origin.x、frame.size.width + frame.origin.x -
[建議] 單行字符數(shù)限制不超過(guò)150個(gè),超出需要換行(空格可以除外),換行時(shí)遵循如下原則:
Tips: 1. 第二行相對(duì)第一行縮進(jìn)4個(gè)空格,從第三行起不再繼續(xù)縮進(jìn)。 2. 運(yùn)算符與下文一起換行。 3. 方法調(diào)用的點(diǎn)符號(hào)與下文一起換行。 正例: - (void)setImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder options:(YYWebImageOptions)options progress:(nullable YYWebImageProgressBlock)progress ransform:(nullable YYWebImageTransformBlock)transform completion:(nullable YYWebImageCompletionBlock)completion; [建議] 不可變對(duì)象盡量使用
copy修飾,如果重寫(xiě)使用copy修飾的set方法,請(qǐng)注意調(diào)用copy方法。[建議] 對(duì)于一些體積小并且重要的信息,不要頻繁的存儲(chǔ)到本地,可以使用
NSUserDefaults進(jìn)行存儲(chǔ)。它會(huì)在合適的時(shí)機(jī)存儲(chǔ)到本地,這避免了頻繁的寫(xiě)入操作,而且在某些極端情況下它也能保證數(shù)據(jù)存儲(chǔ)到本地(例如程序閃退等情況)。[建議] 在多線程環(huán)境下謹(jǐn)慎使用可變集合,必要時(shí)候可以采用加鎖或
GCD的同步線程進(jìn)行保護(hù),或者在訪問(wèn)可變集合時(shí)先將其copy為不可變對(duì)象然后再對(duì)其訪問(wèn)。[建議] 頭文件中盡量不要聲明成員變量而是使用屬性代替。
-
[建議] 頭文件中的屬性盡量聲明為只讀,可以在實(shí)現(xiàn)文件中再將屬性聲明為可讀可寫(xiě)。
正例: @interface WXYZModel : NSObject @property (nonatomic, readonly) NSString *name; @end @interface WXYZModel () @property (nonatomic, strong) NSString *name; @end -
[建議] 不要使用一個(gè)類去維護(hù)多個(gè)類的內(nèi)容,例如一個(gè)常量類維護(hù)所有的常量類,要按常量功能進(jìn)行歸類,分開(kāi)維護(hù)。
Tips: 大而全的類,雜亂無(wú)章,使用查找功能才能定位到具體位置,不利于理解也不利于維護(hù)。 正例: 緩存相關(guān)常量類放在CacheCosts下,系統(tǒng)配置相關(guān)常量類放在SystemConfigConsts下。 [建議] 如果大括號(hào)內(nèi)為空,則簡(jiǎn)潔的寫(xiě)成{}就行。
-
[建議] 沒(méi)有必要增加多余空格來(lái)使上下代碼的等號(hào)對(duì)齊。
正例: int a1 = 1; long a2 = 3; NSString *a3 = @""; 反例: int a1 = 1; long a2 = 3; NSString *a3 = @""; -
[建議] 少用
if else,可以使用if return替換,if嵌套最好不超過(guò)5層。正例: if (x == 1) { …… return; } if (x == 2) { …… return; } 反例: if (x == 1) { …… } else if (x == 2) { …… } -
[建議] 盡量避免采用取反邏輯運(yùn)算符,因?yàn)槿》催壿嫴焕诳焖倮斫狻?/p>
正例: if (array == nil) { …… } 反例: if (!array) { …… } [建議] 如果用到了很多協(xié)議,必要時(shí)可以把協(xié)議封裝到一個(gè)單獨(dú)的頭文件中,這樣做不僅可以減小編譯時(shí)間,還能避免循環(huán)引用。
[建議] 使用Switch枚舉時(shí)盡量將所有枚舉類型都列舉出來(lái)而不使用
default,這樣下次增加枚舉類型時(shí)如果Switch沒(méi)有處理會(huì)有警告信息。-
[建議] 盡量使用字面量語(yǔ)法創(chuàng)建對(duì)象,少用與之等價(jià)的方法。
Tips: OC中的NSArray、NSString、NSDictionay、NSNumber都有與之對(duì)應(yīng)的字面量語(yǔ)法: @[]、@""、@{}、@();使用它們有以下優(yōu)點(diǎn): 1. 簡(jiǎn)單易讀,提高代碼的可讀性和可維護(hù)性。 2. 使用字面量創(chuàng)建數(shù)組、字典時(shí)如果元素里在nil則會(huì)拋出異常,而使用arrayWithObjects:這些等價(jià)方法創(chuàng)建則會(huì)丟失nil后的數(shù)據(jù),拋出異常能讓你知道這里有問(wèn)題及時(shí)修改防止問(wèn)題在線上發(fā)生。 缺點(diǎn): 1. 使用字面量創(chuàng)建的對(duì)象默認(rèn)是不可變的,如果要?jiǎng)?chuàng)建可變對(duì)象需要進(jìn)行mutableCopy操作。 2. 不支持子類,如果你創(chuàng)建了一個(gè)NSString的子類,@""并不會(huì)返回你想要的子類對(duì)象。 [建議] 頭文件中盡量少引用其他頭文件,盡量使用
@class向前聲明,每次引入其他頭文件時(shí)問(wèn)問(wèn)自己是否必須要這樣做。[建議]
UI控件建議使用weak修飾而不是strong修飾。
2. 類編碼規(guī)范
[必須] 如果超類的某個(gè)初始化方法不適用于子類,那么子類一定要覆寫(xiě)超類的這個(gè)方法并解決該問(wèn)題或拋出異常。
[建議] 盡量不要使用
load類方法,如果必須要使用不能在方法內(nèi)實(shí)現(xiàn)復(fù)雜邏輯或堵塞線程。[建議] 盡量減少繼承,類的繼承盡量不要超過(guò)3層,必要時(shí)刻可以考慮用分類、協(xié)議來(lái)代替繼承。
[建議] 把一些穩(wěn)定的、公共的變量或者方法抽取到父類中。子類盡量只維持父類所不具備的特性和功能。
3. 方法編碼規(guī)范
-
[必須] 禁止在
init等初始化方法內(nèi)部、getter、setter、dealloc或其他特殊地方使用.語(yǔ)法訪問(wèn)屬性。Tips: 當(dāng)存在繼承關(guān)系時(shí)使用.語(yǔ)法訪問(wèn)會(huì)因?yàn)槎鄳B(tài)關(guān)系調(diào)用子類的實(shí)現(xiàn)方法,而如果這個(gè)時(shí)候子類還沒(méi)有初始化好或者已經(jīng)釋放了那么可能會(huì)出現(xiàn)一些奇怪的問(wèn)題。 -
[必須] 方法參數(shù)在定義和傳入時(shí),逗號(hào)后面必須添加一個(gè)空格。
正例: method(a1, a2, a3); [建議] 單個(gè)方法的行數(shù)建議不超過(guò)80行,注釋、左右大括號(hào)、空行、回車等除外。
[建議] 在實(shí)現(xiàn)文件內(nèi)部也盡量使用.語(yǔ)法訪問(wèn)屬性而不是使用_直接訪問(wèn)成員變量來(lái)保證風(fēng)格統(tǒng)一。
4. Block編碼規(guī)范
-
[必須] 調(diào)用
Block必須判空處理。Tips: 對(duì)于簡(jiǎn)單的Block可以使用三目運(yùn)算進(jìn)行判空處理, 例如 !self.block ?: self.block(); -
[必須] 在
Block內(nèi)部使用外部變量時(shí)要注意循環(huán)引用的問(wèn)題。Tips: 1. 不一定在Block內(nèi)使用self才會(huì)循環(huán)引用,如下情況也會(huì)造成循環(huán)引用: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { WXYZ_TitleTableViewCell *cell = ……… cell.refreshTableViewBlock = ^{ [tableView reloadData]; }; return cell; } 2. Block內(nèi)部是否要使用weak需要看Block本身和weak的這個(gè)對(duì)象是否存在直接或間接的相互引用,若無(wú)相互引用則不需要使用weak。 3. 如果Block內(nèi)部使用了strong修飾了外部的weak變量,那么當(dāng)使用strong指向成員變量時(shí)需要進(jìn)行判空,否則會(huì)崩潰,參考以下代碼: __weak typeof(self) weakSelf = self; cell.refreshTableViewBlock = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf != nil) { strongSelf->_name = @"name"; } }; 如果把(strongSelf != nil)的判斷去掉那么可能會(huì)崩潰。
5. 通知編碼規(guī)范
[必須] 在發(fā)送通知時(shí),請(qǐng)使用
userInfo進(jìn)行傳值,而不是object。[必須] 通知中心是以同步的方式發(fā)送請(qǐng)求的,所以不要在通知方法做一些復(fù)雜的計(jì)算,特別是當(dāng)它處于主線程的時(shí)候,如果想發(fā)送異步通知可以使用
NSNotificationQueue。[建議] 在工程里能不用通知盡量不用通知,通知雖然靈活強(qiáng)大,但是如果濫用會(huì)導(dǎo)致工程質(zhì)量下降,出現(xiàn)問(wèn)題時(shí)也比較難排查。
6. 注釋編碼規(guī)范
[必須] 與其絞盡腦汁寫(xiě)注釋,不如想想怎么命名;注釋是起輔助作用的,好的命名應(yīng)該是能自我解釋的,如果命名可以解釋其作用,并且方法沒(méi)有任何副作用或者注意事項(xiàng),那么就不用寫(xiě)注釋;注釋?xiě)?yīng)該幫助別人更快的理解該方法的使用和注意事項(xiàng),如果該方法有需要注意的地方一定要在注釋中體現(xiàn)出來(lái)。
[必須] 當(dāng)修改了方法實(shí)現(xiàn)時(shí)需要同步修改注釋內(nèi)容。
[必須] 注釋不要寫(xiě)的太冗長(zhǎng),要簡(jiǎn)單易讀容易理解。
-
[必須] 注釋的雙斜線和內(nèi)容之間有且僅有一個(gè)空格。
正例: // 這是示例注釋,請(qǐng)注意在雙斜線后有一個(gè)空格 - (void)testFunction; [必須] 對(duì)于代碼注釋需謹(jǐn)慎,代碼被注釋一般有2種可能,1) 后續(xù)會(huì)恢復(fù)此段代碼邏輯; 2) 永久不用;對(duì)于第1種情況需添加相應(yīng)注釋,如果沒(méi)有注釋信息難以知曉注釋動(dòng)機(jī),后者建議直接刪除。如果有需要可以通過(guò)代碼倉(cāng)庫(kù)查閱歷史代碼。
-
[必須] 使用特殊注釋標(biāo)記時(shí),請(qǐng)注明標(biāo)記人和標(biāo)記時(shí)間,注意及時(shí)處理這些標(biāo)記。
正例: /** * @brief 簡(jiǎn)要描述 * @author 標(biāo)明開(kāi)發(fā)該類模塊的作者 */ // FIXME: 有bug,需要修改 - (void)testFunction; -
[建議] 別給糟糕的代碼加注釋,重構(gòu)它。
Tips: 注釋不能美化糟糕的代碼。當(dāng)企圖使用注釋前,先考慮是否可以通過(guò)調(diào)整結(jié)構(gòu),命名等操作,消除寫(xiě)注釋的必要。
(三)工程結(jié)構(gòu)規(guī)范
[必須] 局部使用的常量、靜態(tài)變量聲明在
@interface之前。-
[必須]
@property同一類型的聲明放在一塊,不同類型的聲明用2行空格隔開(kāi)。正例: @interface MineViewController () @property (nonatomic, weak) UIView *headView; @property (nonatomic, weak) UITableView *tableView; 我是換行符,請(qǐng)忽略 @property (nonatomic, copy) NSArray *dataSourceArray; -
[必須] 不同邏輯、不同語(yǔ)義、不同業(yè)務(wù)的代碼之間插入一個(gè)空行分隔開(kāi)以提升可讀性。
正例: [self createSubviews]; [self createTableview]; [self netRequest]; -
[必須] 方法歸類
#pragma mark - LifeCycle(生命周期相關(guān)的代碼放在最上面) - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} #pragma mark - Public(公開(kāi)方法) // code... // 上空一行 // 下空兩行 #pragma mark - Private(私有方法) #pragma mark - Override(需要覆蓋父類的方法) #pragma mark - Notification(通知方法) #pragma mark - Delegate(Delegate需要實(shí)現(xiàn)的方法) #pragma mark - getter/setter
結(jié)語(yǔ)
這只是一篇關(guān)于iOS的代碼規(guī)范,所以某些需要和服務(wù)端需要統(tǒng)一的規(guī)范(例如錯(cuò)誤碼)并沒(méi)有提到,還有些關(guān)于如何編寫(xiě)安全代碼方面的規(guī)范也只是略微提到,因?yàn)殛P(guān)于如何寫(xiě)出更安全的代碼應(yīng)該不屬于代碼規(guī)范層面;歡迎大家提出更好的建議或改進(jìn),我也會(huì)不斷更新完善;最后祝大家碼出開(kāi)心,碼出質(zhì)量。