一篇走心的iOS代碼規(guī)范!

前言

關(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ó)際通用名稱或者地名人名除外(例如alibabataobao、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傳遞出去,如果selfretain,到下個(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、setterdealloc或其他特殊地方使用.語(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ì)量。

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過(guò)簡(jiǎn)信或評(píng)論聯(lián)系作者。

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

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