iOS 端编码规范文档

iOS端编码规范

Object-C 篇

基本格式

  • 由于 Xcode 自动补全功能,在Object-C中,命名的清晰比简洁更重要,所以命名应尽可能的清晰
  • 使用 #import 引入 oc/oc++ 头文件,使用 #include 引入 c/c++ 头文件
  • 尽量精简你的公开api接口。无需公开的方法应为私有
  • 任意函数长度不得超过50行
  • 任意行代码不得超过80字符
  • 在定义函数的行前留白一行
  • 功能相近的代码放在相邻的地方
  • 使用 #pragma Mark 区分不同功能的代码块
  • 常量使用字母 k 开头
  • 使用驼峰命名法定义变量,首字母小写,后续单词首字母大写,变量名字母数量不超20个
  • 命名需要和真实意义相关,不能使用无关的单词或无意义的字母命名
  • 命名实例变量在变量前加上_前缀
  • 定义一组相关的常量时,尽量使用枚举类型 建议使用 NS_ENUMNS_OPTIONS 宏来定义枚举类型
  • 使用 const定义浮点型或单个的整数型常量
  • 二元运算符和参数之间需要放置一个空格,一元运算符、强制类型转换和参数之间不放置空格。关键字之后圆括号之前需要放置一个空格
  • 函数名参数过多时,按照 :对齐分行显示
  • 定义长的字面值时同函数名规范一致,按照 ,分行显示,如:数组 字典 等
  • 宏定义字母全部大写
  • 类名的定义应与实际相关并有所区分,自定义视图类在结尾处加上对应的类型,如:自定义UITableViewCell简写为 xxxxCell.h, UICollectionViewCell 简写为 xxxxCCell
  • Object-C 没有命名空间的概念,通常在每个类名前加两个或不超过三个自定义固定大写字母来代替
  • 修饰符 * & 等应紧靠变量,例如:NSString *testString , NSArray *testArray
  • 构造字典时,字典的Key和Value与中间的冒号:都要留有一个空格,多行书写时,也可以将Value对齐
  • 如果构造代码不写在一行内,构造元素需要使用两个空格来进行缩进,右括号]或者}写在新的一行,并且与调用语法糖那行代码的第一个非空字符对齐

代码风格

注释

  • 方法、函数、类、协议、类别的定义都需要注释,推荐采用Apple的标准注释风格,快捷键 cmd + Alt + /自动弹出
  • 定义在头文件里的接口方法、属性必须要有注释!

带参无返回值函数注释:

/**
 *  说明这个函数的主要作用
 *
 *  @param type       type参数的说明
 *  @param macAddress address参数的说明
 */
- (void)refreshConnectorWithConnectType:(IPCConnectType)type  Mac:(NSString *)macAddress;

无参无返回值函数注释:

/**
 *  说明这个函数的主要作用
 */
-(void)stopRunning;

带参有返回值函数注释:

/**
 *  说明这个函数的主要作用
 *
 *  @param macAddress  参数的说明.
 *
 *  @return 返回值的说明
 */
-(IPCCloudDevice *)getCloudDeviceWithMac:(NSString *)macAddress;

协议、委托的注释要明确说明起触发的条件:

/** 代理:说明该方法触发的条件,如:xxx验证失败时发送 */
-(void)initConnectionDidFailed:(IPCConnectHandler *)handler;

如在注释中要引用参数名或者方法函数名, 使用 || 将参数或者方法括起来以避免歧义

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

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

编码风格

  • 非必要尽量不要嵌套 if 语句,提前使用 return 可以避免增加循环的复杂度 并提高代码的可读性
    *** 推荐写法 ***
- (void)someMethod {
  if (![someOther boolValue]) {
      return;
  }

  //Do something important
}

*** 不推荐 ***

- (void)someMethod {
  if ([someOther boolValue]) {
    //Do something important
  }
}
  • 复杂的 if 语句名或表达式,应先提取赋值给一个 BOOL 变量,这样可以使逻辑更清晰,并能让每个子句的意义体现出来
BOOL nameContainsSwift  = [sessionName containsString:@"Swift"];
BOOL isCurrentYear      = [sessionDateCompontents year] == 2014;
BOOL isSwiftSession     = nameContainsSwift && isCurrentYear;

if (isSwiftSession) {
    // Do something very cool
}
  • 三元运算符中的 ? 应只用在它能让代码更加清楚的地方
result = a > b ? x : y;
  • 使用 #pragma mark -来分离不同功能组的方法、protocols 的实现、对父类方法的重写
- (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 { /* ... */ }
  • 初始化类时尽量避免使用 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];
    }
}];
  • 对于参数过多的函数,如不能保持所有参数在同一行时,应每行一个参数,以冒号对齐,如:
-(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;
  • 在分行时,如果第一段名称过短,后续名称可以以Tab的长度(4个空格)为单位进行缩进:
- (void)short:(GTMFoo *)theFoo
        longKeyword:(NSRect)theRect
  evenLongerKeyword:(float)theInterval
              error:(NSError **)theError {
    ...
}
  • 函数调用的格式和书写差不多,可以按照函数的长短来选择写在一行或者分成多行
  • 每一个文件中只创建或者实现一个类, 同一个文件中不能存在多个类
  • Protocol单独用一个文件来创建。尽量不要与相关类混在一个文件中
  • 协议 Protocol 应明确实是否必须或可选,列出 @required , @optional
  • 类的私有变量以_开头
  • 外部引用对象,当外部不会发生 set 操作的对象,使用 readonly 属性。比如:自定义界面创建界面元素时
  • 使用 UITableViewUITableViewCell 时考虑cell 被复用的情况,尽量不要在 delegate 中为 cell 添加 View,推荐使用子类化 利于 Cell充用和对Cell内新添加的子View的布局
  • Objective-C中向nil对象发送命令是不会抛出异常或者导致崩溃的,只是完全的“什么都不干” 所以 nil 检查应参照如下:
//正确,直接判断
if (!objc) {
    ... 
}

//错误,不要使用nil == Object的形式
if (nil == objc) {
    ... 
}
  • 代理要使用 weak 弱引用
  • 推荐的代码缩进和换行方式如下:
if (user.isHappy) {
  //Do something
} else {
  //Do something else
}

/// 不推荐如下缩进:
if (user.isHappy)
{
    //Do something
}
else {
    //Do something else
}

Swift 篇

基础格式

  • 声明变量和常量的数量一行一个,不可同一行声明两个变量或常量,以利于添加注释
  • 变量或常量的数据类型,应尽可能采用类型推断,使代码简洁,如不是默认数据类型时,需要明确声明变量或常量的数据类型
  • 在声明变量指定数据类型时,:冒号与变量名之间没有空格,与数据类型之间保留一个空格
  • 常量,变量,函数,方法的命名规则使用小驼峰规则,首字母小写
  • 类别名称(类、结构体、枚举和协议)使用大驼峰规则,首字母大写。
  • 宏名采用全大写 + 下划线分割单词的风格
  • 使用前缀 k + 大骆驼命名法 为所有非单例的静态常量命名。
  • bool类型变量命名时,建议以is作为前缀
  • 在创建自定义代理方法时,第一个未命名的参数应该是代理源
  • 缩写和简写只能使用常用的或者约定俗成的缩写,缩写和简写中的所有字符的大小写要一致
  • 推荐使用编译器推断的上下文来编写更加简短清晰的代码,如设置颜色时直接使用 .red,而不是UIColor.red
  • 使用懒加载来细致地控制对象的生命周期
  • 可选类型拆包取值时,先使用if let判断
  • 多个可选类型拆包取值时,将多个if let 判断合并
  • 尽量不要使用 as! 或 try! ,对于可选类型Optional多使用as? ,?? 可以给变量设置默认值
  • 数组和字典变量定义时需要标明泛型类型,并使用更简洁清晰的语法
  • 常量定义,建议尽可能定义在类型里面,避免污染全局命名空间,如果是其他地方有可能复用的可以定义在类型外面
  • 优先使用guard来确保条件判断的简短
  • 逗号后面加一个空格,如数组: let array = [1, 2, 3, 4, 5]
  • 字典的书写规范为: 与冒号前的key不留空格,而与Value应保留一个空格,逗号后同样保留一个空格,多个键值对时,应分行显示,以第一个key的分号对齐
  • 方法、if、switch等左大括号不要另起一行,跟随语句放在行末,前置1空格,右大括号独占一行,除非后面跟着统一语句的剩余部分(do while、if else 等)
  • 判断语句不用加括号
  • 尽量不使用self,除非形参与属性同名
  • 访问枚举类型时,使用更简洁的点语法
  • 推荐使用// MARK: - ,按功能、协议、代理等分组
  • 函数头注释时推荐使用 Xcode 注释快捷键 (⌘⌥/)
  • 代码注释放于对应代码的上方或者右边,注释符与注释内容间空1格,右置注释与前面代码空1格,代码上方的注释,应与对应代码保持一样的缩进。
  • 函数的设计应避免函数过长,尽量不能超过50行,参数建议不超过5个,并应避免代码块嵌套过深,建议不要超过4层
  • 当调用的函数有多个参数时,每一个参数另起一行,并比函数名多一个缩进
  • 当数组或字典内容较多需要多行显示时,需把 [ 与数组名或字典名同行,[ 前与 : 保留一个空格 ,结尾的] 做单独一行处理
  • 在调用闭包时,为避免循环引用,闭包内使用弱引用;为避免弱引用被提前释放,多次引用前使用强引用转换
  • 当闭包是函数的最后一个参数,采用尾随闭包写法
  • 当单个闭包表达式上下文清晰时,使用隐式的返回值
  • 单行最多不超过100个字符,超出时应换行展示
  • Switch 模块中不用显式使用break

代码风格

  • 当需要遍历一个集合并变形成另一个集合时,推荐使用函数 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)
     }
}
  • 如果需要把 访问修饰符 放到第一个位置,如:
// 推荐
private static let kMyPrivateNumber: Int

// 不推荐
static private let kMyPrivateNumber: Int
  • 访问修饰符不应单独另起一行,应和访问修饰符描述的对象保持在同一行
// 推荐
public class Pirate {
     /* ... */
}

// 不推荐
public
class Pirate {
     /* ... */
}
  • 使用 Switch 时,不需要使用 break 关键词,如果default 的选项不应该触发,可以抛出错误 或 断言类似的做法
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 中的旧值和新值虽然可以自定义名称,但推荐使用默认标准名称 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)" )
     }
}
  • 声明单例属性可以通过下面方式进行:
class PirateManager {
     static let sharedInstance = PirateManager()
     /* ... */
}
  • 在创建类常量的时候,使用 static 关键词修饰
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)
     }
}
  • 同样在解析可选类型时,推荐使用 guard 语句,而不是 if 语句,因为 guard 语句可以减少不必要的嵌套缩进
// 推荐
guard let monkeyIsland = monkeyIsland  else  {
     return
}
bookVacation(onIsland: monkeyIsland)
bragAboutVacation(onIsland: monkeyIsland)


// 不推荐
if  let monkeyIsland = monkeyIsland {
     bookVacation(onIsland: monkeyIsland)
     bragAboutVacation(onIsland: monkeyIsland)
}
  • 如果需要在2个状态间做出选择,建议使用if 语句,而不是使用 guard 语句
// 推荐
if  isFriendly {
     print( "你好!" )
}  else  {
     print("你哪儿来的?")
}


// 不推荐
guard isFriendly  else  {
     print( "你哪儿来的?" )
     return
}
print( "你好!" )

  • 遇到使用 guard 语句拆包多个可选值时,如果所有拆包失败的错误处理都一致可以把拆包组合到一起 (如 return, break, continue,throw 等). 依据使用场景而决定。
// 组合在一起因为可能立即返回
guard let thingOne = thingOne,
     let thingTwo = thingTwo,
     let thingThree = thingThree  else  {
     return
}


// 使用独立的语句 因为每个场景返回不同的错误
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." )
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容

  • 移动端iOS开发规范文档 序言 根据网上的一些OC编码规范整理归纳而成,为了利于项目维护以及规范开发,促进成员之间...
    Living_U阅读 699评论 0 1
  • 移动端iOS开发规范文档 目录 格式与换行 命名 Objective-C下的cocoa编码规范 注释要求 其他 参...
    志城阅读 4,705评论 1 6
  • 朗境科技 移动团队 郎镜通代码规范指南 介绍 关于这个编程语言的所有规范,如果这里没有写到,那就在苹果的文档里:...
    百事小武阅读 839评论 1 2
  • iOS 代码规范文档 [toc] 修订 概述 制定目的:制定iOS 编码规范,主要是为了规范公司内部的iOS 代码...
    喜相逢v5阅读 1,089评论 0 4
  • Objective-C 编码规范,内容来自苹果的文档翻译,自己的编码经验和对其它资料的总结。 一.命名规范 基本原...
    爱搞事的Snoopy阅读 449评论 0 0