iOS 端編碼規(guī)范文檔

iOS端編碼規(guī)范

Object-C 篇

基本格式

  • 由于 Xcode 自動補(bǔ)全功能,在Object-C中,命名的清晰比簡潔更重要,所以命名應(yīng)盡可能的清晰
  • 使用 #import 引入 oc/oc++ 頭文件,使用 #include 引入 c/c++ 頭文件
  • 盡量精簡你的公開api接口。無需公開的方法應(yīng)為私有
  • 任意函數(shù)長度不得超過50行
  • 任意行代碼不得超過80字符
  • 在定義函數(shù)的行前留白一行
  • 功能相近的代碼放在相鄰的地方
  • 使用 #pragma Mark 區(qū)分不同功能的代碼塊
  • 常量使用字母 k 開頭
  • 使用駝峰命名法定義變量,首字母小寫,后續(xù)單詞首字母大寫,變量名字母數(shù)量不超20個(gè)
  • 命名需要和真實(shí)意義相關(guān),不能使用無關(guān)的單詞或無意義的字母命名
  • 命名實(shí)例變量在變量前加上_前綴
  • 定義一組相關(guān)的常量時(shí),盡量使用枚舉類型 建議使用 NS_ENUMNS_OPTIONS 宏來定義枚舉類型
  • 使用 const定義浮點(diǎn)型或單個(gè)的整數(shù)型常量
  • 二元運(yùn)算符和參數(shù)之間需要放置一個(gè)空格,一元運(yùn)算符、強(qiáng)制類型轉(zhuǎn)換和參數(shù)之間不放置空格。關(guān)鍵字之后圓括號之前需要放置一個(gè)空格
  • 函數(shù)名參數(shù)過多時(shí),按照 :對齊分行顯示
  • 定義長的字面值時(shí)同函數(shù)名規(guī)范一致,按照 ,分行顯示,如:數(shù)組 字典 等
  • 宏定義字母全部大寫
  • 類名的定義應(yīng)與實(shí)際相關(guān)并有所區(qū)分,自定義視圖類在結(jié)尾處加上對應(yīng)的類型,如:自定義UITableViewCell簡寫為 xxxxCell.h, UICollectionViewCell 簡寫為 xxxxCCell
  • Object-C 沒有命名空間的概念,通常在每個(gè)類名前加兩個(gè)或不超過三個(gè)自定義固定大寫字母來代替
  • 修飾符 * & 等應(yīng)緊靠變量,例如:NSString *testString , NSArray *testArray
  • 構(gòu)造字典時(shí),字典的Key和Value與中間的冒號:都要留有一個(gè)空格,多行書寫時(shí),也可以將Value對齊
  • 如果構(gòu)造代碼不寫在一行內(nèi),構(gòu)造元素需要使用兩個(gè)空格來進(jìn)行縮進(jìn),右括號]或者}寫在新的一行,并且與調(diào)用語法糖那行代碼的第一個(gè)非空字符對齊

代碼風(fēng)格

注釋

  • 方法、函數(shù)、類、協(xié)議、類別的定義都需要注釋,推薦采用Apple的標(biāo)準(zhǔn)注釋風(fēng)格,快捷鍵 cmd + Alt + /自動彈出
  • 定義在頭文件里的接口方法、屬性必須要有注釋!

帶參無返回值函數(shù)注釋:

/**
 *  說明這個(gè)函數(shù)的主要作用
 *
 *  @param type       type參數(shù)的說明
 *  @param macAddress address參數(shù)的說明
 */
- (void)refreshConnectorWithConnectType:(IPCConnectType)type  Mac:(NSString *)macAddress;

無參無返回值函數(shù)注釋:

/**
 *  說明這個(gè)函數(shù)的主要作用
 */
-(void)stopRunning;

帶參有返回值函數(shù)注釋:

/**
 *  說明這個(gè)函數(shù)的主要作用
 *
 *  @param macAddress  參數(shù)的說明.
 *
 *  @return 返回值的說明
 */
-(IPCCloudDevice *)getCloudDeviceWithMac:(NSString *)macAddress;

協(xié)議、委托的注釋要明確說明起觸發(fā)的條件:

/** 代理:說明該方法觸發(fā)的條件,如:xxx驗(yàn)證失敗時(shí)發(fā)送 */
-(void)initConnectionDidFailed:(IPCConnectHandler *)handler;

如在注釋中要引用參數(shù)名或者方法函數(shù)名, 使用 || 將參數(shù)或者方法括起來以避免歧義

// Sometimes we need |count| to be less than zero.

// Remember to call |StringWithoutSpaces("foo bar baz")|

編碼風(fēng)格

  • 非必要盡量不要嵌套 if 語句,提前使用 return 可以避免增加循環(huán)的復(fù)雜度 并提高代碼的可讀性
    *** 推薦寫法 ***
- (void)someMethod {
  if (![someOther boolValue]) {
      return;
  }

  //Do something important
}

*** 不推薦 ***

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}
  • 復(fù)雜的 if 語句名或表達(dá)式,應(yīng)先提取賦值給一個(gè) BOOL 變量,這樣可以使邏輯更清晰,并能讓每個(gè)子句的意義體現(xiàn)出來
BOOL nameContainsSwift  = [sessionName containsString:@"Swift"];
BOOL isCurrentYear      = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession     = nameContainsSwift && isCurrentYear;

if (isSwiftSession) {
    // Do something very cool
}
  • 三元運(yùn)算符中的 ? 應(yīng)只用在它能讓代碼更加清楚的地方
result = a > b ? x : y;
  • 使用 #pragma mark -來分離不同功能組的方法、protocols 的實(shí)現(xiàn)、對父類方法的重寫
- (void)dealloc { /* ... */ }
- (instancetype)init { /* ... */ }

#pragma mark - View Lifecycle (View 的生命周期)

- (void)viewDidLoad { /* ... */ }
- (void)viewWillAppear:(BOOL)animated { /* ... */ }
- (void)didReceiveMemoryWarning { /* ... */ }

#pragma mark - Custom Accessors (自定義訪問器)

- (void)setCustomProperty:(id)value { /* ... */ }
- (id)customProperty { /* ... */ }

#pragma mark - IBActions  

- (IBAction)submitData:(id)sender { /* ... */ }

#pragma mark - Public 

- (void)publicMethod { /* ... */ }

#pragma mark - Private

- (void)zoc_privateMethod { /* ... */ }

#pragma mark - UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }

#pragma mark - ZOCSuperclass

// ... 重載來自 ZOCSuperclass 的方法

#pragma mark - NSObject

- (NSString *)description { /* ... */ }
  • 初始化類時(shí)盡量避免使用 new 方法,采用 alloc init
  • 使用 dispatch_once 來生成單例, 單例類方法名命名保持一致性。
+ (instancetype)sharedInstance 
{ 
 static id sharedInstance = nil; 
 static dispatch_once_t onceToken = 0;
       dispatch_once(&onceToken, ^{ 
  sharedInstance = [[self alloc] init];
  }); 
 return sharedInstance; 
} 
  • Block 正確使用 __weakSelf, 參考如下:
/// 單語句使用
__weak __typeof(self) weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
    [weakSelf doSomethingWithData:data];
}];


/// 多語句使用
__weak __typeof(self)weakSelf = self;
[self executeBlock:^(NSData *data, NSError *error) {
    __strong __typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        [strongSelf doSomethingWithData:data];
        [strongSelf doSomethingWithData:data];
    }
}];
  • 對于參數(shù)過多的函數(shù),如不能保持所有參數(shù)在同一行時(shí),應(yīng)每行一個(gè)參數(shù),以冒號對齊,如:
-(id)initWithModel:(IPCModle)model
       ConnectType:(IPCConnectType)connectType
        Resolution:(IPCResolution)resolution
          AuthName:(NSString *)authName
          Password:(NSString *)password
               MAC:(NSString *)mac
              AzIp:(NSString *)az_ip
             AzDns:(NSString *)az_dns
             Token:(NSString *)token
             Email:(NSString *)email
          Delegate:(id<IPCConnectHandlerDelegate>)delegate;
  • 在分行時(shí),如果第一段名稱過短,后續(xù)名稱可以以Tab的長度(4個(gè)空格)為單位進(jìn)行縮進(jìn):
- (void)short:(GTMFoo *)theFoo
        longKeyword:(NSRect)theRect
  evenLongerKeyword:(float)theInterval
              error:(NSError **)theError {
    ...
}
  • 函數(shù)調(diào)用的格式和書寫差不多,可以按照函數(shù)的長短來選擇寫在一行或者分成多行
  • 每一個(gè)文件中只創(chuàng)建或者實(shí)現(xiàn)一個(gè)類, 同一個(gè)文件中不能存在多個(gè)類
  • Protocol單獨(dú)用一個(gè)文件來創(chuàng)建。盡量不要與相關(guān)類混在一個(gè)文件中
  • 協(xié)議 Protocol 應(yīng)明確實(shí)是否必須或可選,列出 @required , @optional
  • 類的私有變量以_開頭
  • 外部引用對象,當(dāng)外部不會發(fā)生 set 操作的對象,使用 readonly 屬性。比如:自定義界面創(chuàng)建界面元素時(shí)
  • 使用 UITableViewUITableViewCell 時(shí)考慮cell 被復(fù)用的情況,盡量不要在 delegate 中為 cell 添加 View,推薦使用子類化 利于 Cell充用和對Cell內(nèi)新添加的子View的布局
  • Objective-C中向nil對象發(fā)送命令是不會拋出異?;蛘邔?dǎo)致崩潰的,只是完全的“什么都不干” 所以 nil 檢查應(yīng)參照如下:
//正確,直接判斷
if (!objc) {
    ... 
}

//錯(cuò)誤,不要使用nil == Object的形式
if (nil == objc) {
    ... 
}
  • 代理要使用 weak 弱引用
  • 推薦的代碼縮進(jìn)和換行方式如下:
if (user.isHappy) {
  //Do something
} else {
  //Do something else
}

/// 不推薦如下縮進(jìn):
if (user.isHappy)
{
    //Do something
}
else {
    //Do something else
}

Swift 篇

基礎(chǔ)格式

  • 聲明變量和常量的數(shù)量一行一個(gè),不可同一行聲明兩個(gè)變量或常量,以利于添加注釋
  • 變量或常量的數(shù)據(jù)類型,應(yīng)盡可能采用類型推斷,使代碼簡潔,如不是默認(rèn)數(shù)據(jù)類型時(shí),需要明確聲明變量或常量的數(shù)據(jù)類型
  • 在聲明變量指定數(shù)據(jù)類型時(shí),:冒號與變量名之間沒有空格,與數(shù)據(jù)類型之間保留一個(gè)空格
  • 常量,變量,函數(shù),方法的命名規(guī)則使用小駝峰規(guī)則,首字母小寫
  • 類別名稱(類、結(jié)構(gòu)體、枚舉和協(xié)議)使用大駝峰規(guī)則,首字母大寫。
  • 宏名采用全大寫 + 下劃線分割單詞的風(fēng)格
  • 使用前綴 k + 大駱駝命名法 為所有非單例的靜態(tài)常量命名。
  • bool類型變量命名時(shí),建議以is作為前綴
  • 在創(chuàng)建自定義代理方法時(shí),第一個(gè)未命名的參數(shù)應(yīng)該是代理源
  • 縮寫和簡寫只能使用常用的或者約定俗成的縮寫,縮寫和簡寫中的所有字符的大小寫要一致
  • 推薦使用編譯器推斷的上下文來編寫更加簡短清晰的代碼,如設(shè)置顏色時(shí)直接使用 .red,而不是UIColor.red
  • 使用懶加載來細(xì)致地控制對象的生命周期
  • 可選類型拆包取值時(shí),先使用if let判斷
  • 多個(gè)可選類型拆包取值時(shí),將多個(gè)if let 判斷合并
  • 盡量不要使用 as! 或 try! ,對于可選類型Optional多使用as? ,?? 可以給變量設(shè)置默認(rèn)值
  • 數(shù)組和字典變量定義時(shí)需要標(biāo)明泛型類型,并使用更簡潔清晰的語法
  • 常量定義,建議盡可能定義在類型里面,避免污染全局命名空間,如果是其他地方有可能復(fù)用的可以定義在類型外面
  • 優(yōu)先使用guard來確保條件判斷的簡短
  • 逗號后面加一個(gè)空格,如數(shù)組: let array = [1, 2, 3, 4, 5]
  • 字典的書寫規(guī)范為: 與冒號前的key不留空格,而與Value應(yīng)保留一個(gè)空格,逗號后同樣保留一個(gè)空格,多個(gè)鍵值對時(shí),應(yīng)分行顯示,以第一個(gè)key的分號對齊
  • 方法、if、switch等左大括號不要另起一行,跟隨語句放在行末,前置1空格,右大括號獨(dú)占一行,除非后面跟著統(tǒng)一語句的剩余部分(do while、if else 等)
  • 判斷語句不用加括號
  • 盡量不使用self,除非形參與屬性同名
  • 訪問枚舉類型時(shí),使用更簡潔的點(diǎn)語法
  • 推薦使用// MARK: - ,按功能、協(xié)議、代理等分組
  • 函數(shù)頭注釋時(shí)推薦使用 Xcode 注釋快捷鍵 (??/)
  • 代碼注釋放于對應(yīng)代碼的上方或者右邊,注釋符與注釋內(nèi)容間空1格,右置注釋與前面代碼空1格,代碼上方的注釋,應(yīng)與對應(yīng)代碼保持一樣的縮進(jìn)。
  • 函數(shù)的設(shè)計(jì)應(yīng)避免函數(shù)過長,盡量不能超過50行,參數(shù)建議不超過5個(gè),并應(yīng)避免代碼塊嵌套過深,建議不要超過4層
  • 當(dāng)調(diào)用的函數(shù)有多個(gè)參數(shù)時(shí),每一個(gè)參數(shù)另起一行,并比函數(shù)名多一個(gè)縮進(jìn)
  • 當(dāng)數(shù)組或字典內(nèi)容較多需要多行顯示時(shí),需把 [ 與數(shù)組名或字典名同行,[ 前與 : 保留一個(gè)空格 ,結(jié)尾的] 做單獨(dú)一行處理
  • 在調(diào)用閉包時(shí),為避免循環(huán)引用,閉包內(nèi)使用弱引用;為避免弱引用被提前釋放,多次引用前使用強(qiáng)引用轉(zhuǎn)換
  • 當(dāng)閉包是函數(shù)的最后一個(gè)參數(shù),采用尾隨閉包寫法
  • 當(dāng)單個(gè)閉包表達(dá)式上下文清晰時(shí),使用隱式的返回值
  • 單行最多不超過100個(gè)字符,超出時(shí)應(yīng)換行展示
  • Switch 模塊中不用顯式使用break

代碼風(fēng)格

  • 當(dāng)需要遍歷一個(gè)集合并變形成另一個(gè)集合時(shí),推薦使用函數(shù) map, filter 和 reduce
// 推薦
let stringOfInts = [1, 2, 3].flatMap { String($0) }
// ["1", "2", "3"]

// 不推薦
var  stringOfInts: [String] = []
for  integer  in  [1, 2, 3] {
     stringOfInts.append(String(integer))
}


// 推薦
let evenNumbers = [4, 8, 15, 16, 23, 42].filter { $0 % 2 == 0 }
// [4, 8, 16, 42]

// 不推薦
var  evenNumbers: [Int] = []
for  integer  in  [4, 8, 15, 16, 23, 42] {
     if  integer % 2 == 0 {
         evenNumbers(integer)
     }
}
  • 如果需要把 訪問修飾符 放到第一個(gè)位置,如:
// 推薦
private static let kMyPrivateNumber: Int

// 不推薦
static private let kMyPrivateNumber: Int
  • 訪問修飾符不應(yīng)單獨(dú)另起一行,應(yīng)和訪問修飾符描述的對象保持在同一行
// 推薦
public class Pirate {
     /* ... */
}

// 不推薦
public
class Pirate {
     /* ... */
}
  • 使用 Switch 時(shí),不需要使用 break 關(guān)鍵詞,如果default 的選項(xiàng)不應(yīng)該觸發(fā),可以拋出錯(cuò)誤 或 斷言類似的做法
func handleDigit(digit: Int) throws {
     case  0, 1, 2, 3, 4, 5, 6, 7, 8, 9:
         print( "Yes, \(digit) is a digit!" )
     default :
         throw  Error(message:  "The given number was not a digit." )
}
  • 對于willSet/didSet 和 set 中的舊值和新值雖然可以自定義名稱,但推薦使用默認(rèn)標(biāo)準(zhǔn)名稱 newValue/oldValue
var  computedProperty: String {
     get {
         if  someBool {
             return  "I'm a mighty pirate!"
         }
         return  "I'm selling these fine leather jackets."
     }
     set {
         computedProperty = newValue
     }
     willSet {
         print( "will set to \(newValue)" )
     }
     didSet {
         print( "did set from \(oldValue) to \(newValue)" )
     }
}
  • 聲明單例屬性可以通過下面方式進(jìn)行:
class PirateManager {
     static let sharedInstance = PirateManager()
     /* ... */
}
  • 在創(chuàng)建類常量的時(shí)候,使用 static 關(guān)鍵詞修飾
class MyTableViewCell: UITableViewCell {
     static let kReuseIdentifier = String(MyTableViewCell)
     static let kCellHeight: CGFloat = 80.0
}
  • 使用 guard 語句提前返回的策略替代 if 語句的嵌套 來改善代碼的可讀性
// 推薦
func eatDoughnut(atIndex index: Int) {
     guard index >= 0 && index < doughnuts  else  {
         // 如果 index 超出允許范圍,提前返回。
         return
     }
     let doughnut = doughnuts[index]
     eat(doughnut)
}


// 不推薦
func eatDoughnuts(atIndex index: Int) {
     if  index >= 0 && index < donuts.count {
         let doughnut = doughnuts[index]
         eat(doughnut)
     }
}
  • 同樣在解析可選類型時(shí),推薦使用 guard 語句,而不是 if 語句,因?yàn)?guard 語句可以減少不必要的嵌套縮進(jìn)
// 推薦
guard let monkeyIsland = monkeyIsland  else  {
     return
}
bookVacation(onIsland: monkeyIsland)
bragAboutVacation(onIsland: monkeyIsland)


// 不推薦
if  let monkeyIsland = monkeyIsland {
     bookVacation(onIsland: monkeyIsland)
     bragAboutVacation(onIsland: monkeyIsland)
}
  • 如果需要在2個(gè)狀態(tài)間做出選擇,建議使用if 語句,而不是使用 guard 語句
// 推薦
if  isFriendly {
     print( "你好!" )
}  else  {
     print("你哪兒來的?")
}


// 不推薦
guard isFriendly  else  {
     print( "你哪兒來的?" )
     return
}
print( "你好!" )

  • 遇到使用 guard 語句拆包多個(gè)可選值時(shí),如果所有拆包失敗的錯(cuò)誤處理都一致可以把拆包組合到一起 (如 return, break, continue,throw 等). 依據(jù)使用場景而決定。
// 組合在一起因?yàn)榭赡芰⒓捶祷?guard let thingOne = thingOne,
     let thingTwo = thingTwo,
     let thingThree = thingThree  else  {
     return
}


// 使用獨(dú)立的語句 因?yàn)槊總€(gè)場景返回不同的錯(cuò)誤
guard let thingOne = thingOne  else  {
     throw  Error(message:  "Unwrapping thingOne failed." )
}
guard let thingTwo = thingTwo  else  {
     throw  Error(message:  "Unwrapping thingTwo failed." )
}
guard let thingThree = thingThree  else  {
     throw  Error(message:  "Unwrapping thingThree failed." )
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 移動端iOS開發(fā)規(guī)范文檔 序言 根據(jù)網(wǎng)上的一些OC編碼規(guī)范整理歸納而成,為了利于項(xiàng)目維護(hù)以及規(guī)范開發(fā),促進(jìn)成員之間...
    Living_U閱讀 808評論 0 1
  • 移動端iOS開發(fā)規(guī)范文檔 目錄 格式與換行 命名 Objective-C下的cocoa編碼規(guī)范 注釋要求 其他 參...
    志城閱讀 4,782評論 1 6
  • 朗境科技 移動團(tuán)隊(duì) 郎鏡通代碼規(guī)范指南 介紹 關(guān)于這個(gè)編程語言的所有規(guī)范,如果這里沒有寫到,那就在蘋果的文檔里:...
    百事小武閱讀 945評論 1 2
  • iOS 代碼規(guī)范文檔 [toc] 修訂 概述 制定目的:制定iOS 編碼規(guī)范,主要是為了規(guī)范公司內(nèi)部的iOS 代碼...
    喜相逢v5閱讀 1,166評論 0 4
  • Objective-C 編碼規(guī)范,內(nèi)容來自蘋果的文檔翻譯,自己的編碼經(jīng)驗(yàn)和對其它資料的總結(jié)。 一.命名規(guī)范 基本原...
    愛搞事的Snoopy閱讀 532評論 0 0

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