iOS开发规范

iOS开发规范

前言

由于每个开发者的编码习惯和风格都不一样,为了保证开发效率,减轻代码阅读成本,随着以后 APP 业务线越来越大,代码量越来越多时,能保障代码的可维护性,降低维护成本,方便高效定位解决问题,所以从项目的细节到整体都希望根据此文档来达到风格统一,利于维护。

一、命名规范

不论是类名,属性,变量,常量,方法,命名应该清晰明了,表达准确,能够望名知意,尽量不要使用缩写,更不要使用中文拼音。

1.1 类(Class)

  • 类名统一使用 BC 开头作为前缀,每个单词首字母大写(大驼峰命名)。

示例:

///(C)控制器
BCExchangeRecordController
///(V)视图
BCExchangeRecordCell
///(M)模型
BCExchangeRecordModel

1.2 缩写

通常,我们都不应该缩写命名。然而,下面所列举的都是一些众所周知的缩写,我们可以继续使用这些古老的缩写。在其他情况下,我们需要遵循下面两条缩写建议:

  • 允许使用那些在C语言时代就已经在使用的缩写,比如alloc和getc。
  • 我们可以在命名参数的时候使用缩写。其他情况,尽量不要使用缩写。

我们也可以使用计算机行业通用的缩写。包括但不限于HTML、URL、RTF、HTTP、TIFF、JPG、PNG、GIF、LZW、ROM、RGB、CMYK、MIDI、FTP。

1.3 属性&局部变量&成员变量&参数名

  • ==尽量不要使用成员变量,改为使用属性(property)。==

  • 属性或变量以小写字母开头,后面每个单词首字母大写(小驼峰命名)。

示例:

/// 版本号
@property (nonatomic, copy) NSString *versionNumber;

1.4 宏定义&局部常量&全局常量

  • 局部常量使用小写字母 k 开头,后面单词首字母大写(小驼峰命名)。
  • 常量宏每个字母都采用大写,单词之间可以使用下划线分割。
  • 预编译宏每个字母都采用大写,单词之间可以使用下划线分割。
  • 全局常量使用前缀 BC 开头,后面单词首字母大写(小驼峰命名)。定义在公共的 .h 文件中,在 .m 文件中写实现。

示例:

/// 局部常量,只定义在 .m 文件中,不对外暴露
static const NSTimeInterval kAnimationDuration = 0.25;
static NSString *const kPropertyKey = @"xxxxx";

/// 常量宏
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)

/// 预编译宏
#if RELEASE

#elif DEV

#endif

/**全局常量 */ 
/// 在 .h 文件定义
extern NSString *const BCUpdateUserInfoNotification;

/// 在 .m 文件实现
NSString *const BCUpdateUserInfoNotification = @"BCUpdateUserInfoNotification";

1.5 通知(Notification)

  • 通知(广播)名使用大写字母 BC 开头,后面每个单词首字母大写(小驼峰命名),以 Notification 为后缀。定义在公共的 .h 文件中,在 .m 文件中写实现。

示例:

/// 在 .h 文件定义
extern NSString *const BCUpdateUserInfoNotification;

/// 在 .m 文件实现
NSString *const BCUpdateUserInfoNotification = @"BCUpdateUserInfoNotification";

1.6 枚举(Enum)

使用 NS_ENUM 定义通用枚举,NS_OPTIONS 定义位移枚举
示例:

/// 通用枚举示例
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown,
};

/// 位移枚举示例
typedef NS_OPTIONS(NSUInteger, UIControlState) {
    UIControlStateNormal       = 0,
    UIControlStateHighlighted  = 1 << 0,
    UIControlStateDisabled     = 1 << 1,
};

1.7 方法(Method)

  • 方法名采用小写字母开头的(小驼峰命名)方式。
  • 不要在方法名称中使用前缀,不要使用 “_” 下划线开头。
  • 尽量避免方法参数过多导致方法太长,当参数过多时(比如超过5个),可以封装成模型(Model)对象 或者 使用字典(NSDictionary)传入。

示例:

/** 类方法,无返回值 */
+ (void)appUpdateWithModel:(BCAppUpdateModel *)model;

/** 实例方法,有返回值 */
- (NSObject *)objectWithDictionary:(NSDictionary *)dict;

1.8 类别(Category)

避免category中的方法覆盖系统方法和属性。可以在自定义方法或者属性前加前缀 bc 加下划线来区分。
示例:

/// 属性
@property (nonatomic, strong) NSURL *bc_imageURL;
/// 方法
- (void)bc_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;

二、代码规范

2.1 属性(property)

  • 属性按照不同数据类型和作用,正确使用修饰符。

  • 属性的关键字按照 原子性,读写,内存管理的顺序排列。

  • 属性内存管理修饰符:

    • NSString,block 使用 ==copy== 修饰。

    • OC 对象使用 ==strong==,==weak== 修饰。

    • Delegate 使用 ==weak== 修饰,防止循环引用。

    • 不可变的 NSArrty,NSDictionary,NSAttributedString 可使用 ==copy== 修饰。

    • 可变的 NSMutableArray,NSMutableDictionary,NSMutableAttributedString 必须使用 ==strong== 修饰,防止对其进行可变操作时崩溃。

    • 使用 typedef 重定义的 OC 数据类型(如:NSTimeInterval,NSInteger),枚举类型,普通的基本数据类型使用 ==assign== 修饰。

  • 接口属性定义原则:

    • .h 文件中,只可暴露对外公开且需要被使用的属性,对内使用的写在 .m 文件的类扩展中,务必遵循面向对象的 封装 特性。

    • 对于对内需要读写操作,对外又能使用的属性,只需要在 .h 文件中加上 readonly 修饰符即可,在 .m 文件的类扩展中正常使用 property

    • 集合类:数组(NSArray),字典(NSDictionary)尽量使用泛型来指定对象类型,尤其是 模型数组

    • 对于布尔(BOOL)类型,请尽量提供 getter=isXXX 方法。

  • ==所有的属性都使用getter和setter==
    避免初始化属性的位置比较随意,有单独添加一个初始化方法类似 setupView 的,有在 init 初始化的,各种情况都有,导致团队协作的时候代码显得非常乱。

示例:

/// .h 文件定义
@interface XJWebViewController : UIViewController

/// 是否隐藏 HUD,默认显示,如果隐藏则显示进度条,显示 HUD 时当前 view 不可交互
@property (nonatomic, assign, getter=isHideProgressLoadingView) BOOL hideProgressLoadingView;

/// JS 方法名数组, 用于 js 调用 native
@property (nonatomic, strong, nullable) NSMutableArray <NSString *> *jsMethodArray;

/// wkWebview,对外只读,防止外界对它进行初始化操作
@property (nonatomic, strong, readonly) WKWebView *wkWebView;

/// urlStr,提供给外界赋值
@property (nonatomic, copy) NSString *urlStr;

@end

/// .m 文件的类扩展
@interface XJWebViewController ()

/// 对内可读写操作的 wkWebView
@property (nonatomic, strong) WKWebView *wkWebView;

#pragma mark - getters and setters
 - (void)setWkWebView
 - (WKWebView *)wkWebView
 ....

@end

2.2 委托(protocol)

  • @protocol 的定义,不需要另外新建文件,统一写在当前类 .h 文件的顶部,并将当前类使用 @class xxx; 的方式引入,写在 @protocol 的定义上方。

  • @protocol 命名规则:使用类名+Delegate后缀,如:UITableViewDelegate

  • 使用 @optional 修饰可以不实现的方法,使用 @required 修饰必须实现的方法。

  • 类的实例必须作为回调方法的参数之一。

    • 回调方法的参数只有类自己的情况,方法名要符合实际含义。

    • 回调方法存在两个以上参数的情况,以类的名字开头,以表明此方法是属于哪个类的。

示例:

@class UITableView;

@protocol UITableViewDelegate <NSObject>

@required 
// do something...

@optional
/// 可通过实现此委托方法来设置 cell 的行高。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

@end

@interface UITableView : UIScrollView
/// 委托属性,务必使用 weak 修饰
@property (nonatomic, weak) id<UITableViewDelegate> delegate;

@end

2.3 #import 使用

  • 当一个类需要 import 其它类时,尽量在 .m 文件中 ==#import "xxx.h"==,如果需要在 .h 中引用,可在 .h 文件的顶部使用 @class xxx; 引入,告诉编译器有这个文件。这样做也更遵循面向对象的 封装 特性。

2.4 代码注释

  • 注释统一写在 属性(字段),方法的上方。

  • 模型类的属性(字段),必须写注释,表明该字段的含义及作用。

  • 委托协议方法,闭包(block)回调方法,及每个类(视图类,控制器类,工具类,分类)的 .h 文件中暴露出来的属性,函数接口,都必须写注释,表名属性的作用,方法的用途和使用,及方法各参数的说明。

  • ==属性声明单行注释==,以下两种注释方式可选,注释符和文案之间需要使用空格分开,使用以下两种注释的好处是按住 command 键查看属性时有注释提示。

示例:

/// 注释说明
@property (nonatomic, copy) NSString *placeholder; 

/** 注释说明 */
@property (nonatomic, strong) UIColor *textColor; 
  • ==方法声明多行注释==,使用快捷键 option + command + / 即可自动多行注释。

示例:

/**
 调起第三方支付客户端

 @param info 订单信息,微信(传PayReq对象);支付宝(传加密后的订单字符串)。
 @param callback 回调,返回状态码及状态信息。
 */
+ (void)payWithInfo:(id)info callback:(ResultCallback)callback;
  • ==其它代码内注释==,使用 // 注释 即可,UI,布局等可少些注释,涉及到逻辑和业务的请务必多写注释,方便其他人阅读代码时能快速理解相关的逻辑业务。

  • 对于业务代码注释的内容,相对于 ==“做了什么”==,更应该说明 ==“为什么这么做”==。

三、格式规范

3.1 指针 * 位置

  • 定义对象属性,变量时,指针 * 的位置应该偏向属性(变量)名这边。示例:UITextField<UITextInput> *textInput;

3.2 空格规范

  • 属性的括号,修饰词,属性名之间需要使用空格。

  • 在类方法的 + 号 或者实例方法的 - 号 与返回值之间需要使用空格。

  • 方法名开始位置和返回值括号之间,及结尾处与;之间不需要使用空格。

  • 在实现方法时,方法的结尾处与大括号{之间需要使用空格。

  • 代码块内的条件分支,循环,运算等 (){} 之间需要使用空格。

  • 变量或者其它的操作数之间,进行赋值,运算时都需要使用空格。

3.3 代码块&缩进

  • 统一使用 Xcode 的自动缩进,快捷键 command + A 全选,control + I 缩进。

  • 每个方法的代码行数尽量控制在100行以内,超过100行时可将部分业务或逻辑代码抽取成另一个方法来调用。

  • 避免代码冗余,将多处同样的代码封装成可复用的公共方法。

  • 代码块内尽量减少不必要的空行,保持代码块的紧凑性。

  • 方法与方法之间至少需要保留一行空行。

3.4 大括号写法

  • 方法中{}的使用,包括但不限于 if,for,while,switch 等所有场景,左花括号必须紧跟在第一行代码后面,不要另起一行。

示例:

- (void)loadServersData {
    dispatch_async(dispatch_get_main_queue(), ^{
        for (NSObject *obj in self.dataSource) {
            // do something...  
        }
    });
}

3.5 if else 分支

  • 对于空 nil 条件判断,应使用 ! 运算符,不应使用 == 运算符。

示例:

if (!_tableView) {
    // do something...
}
  • 须列出所有分支(穷举所有的情况),而且每个分支都须给出明确的结果。

示例:

if (/* 条件表达式 */) {
    // do something...
} else {
    // do something...
}
  • 不要使用过多的分支,要善于使用 return 来提前返回错误的情况,把最正确的情况放到最后返回。

示例:

if (!user.UserName || !user.UserName.length) return NO;
if (!user.Password || !user.Password.length) return NO;

// do something...

return YES;
  • 条件过多,过长的时候应该换行。条件表达式如果很长,则需要将他们提取出来赋给一个BOOL值,或者抽取出一个方法。

示例:

if (condition1 && 
    condition2 && 
    condition3 && 
    condition4) {
    // do something...
}

if ([self canDelete]) {
    // do something...
}

- (BOOL)canDelete {
    BOOL finalCondition1 = condition1 && condition2;
    BOOL finalCondition2 = condition3 && condition4;
    return condition1 && condition2;
}

3.6 Switch 语句

  • 每个分支都必须用大括号括起来。

示例:

switch (integer) {  
  case 1:  {
    // do something... 
   }
    break;  
  case 2: {  
    // do something...
    break;  
  }  
  default:{
    // do something...  
    break; 
  }
}
  • 使用枚举类型时,不能有 default 分支, 除了使用枚举类型以外,都必须有 default 分支。

示例:

 switch (self.networkState) {
   case EnumNetworkStateNormal: {
        // do something...
   }
        break;
   case EnumNetworkStateNetError: {
        // do something...
   }
        break;
   case EnumNetworkStateServerError: {
        // do something...
   }
        break;
}

3.7 Method 分组

  • 使用 #pragma mark - 对 Method 进行分组。
@property (nonatomic, strong) UIButton *confirmButton;

#pragma mark - Life cycle
viewDidLoad
viewWillAppear
...

#pragma mark - UITableViewDataSource 
    // do something...
    
#pragma mark - UITableViewDelegate  
    // do something...
    
#pragma mark - CustomDelegate
    // do something...
    
#pragma mark - Event respopense
    // do something...
    
#pragma mark - Public Methods
    // do something...
    
#pragma mark - Private Methods
    // do something...

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