iOS开发规范

前言

为了利于项目维护以及规范开发,促进成员之间Code Review的效率,故提出以下开发规范,如有更好的建议,欢迎提出。


(一)gitlab分支管理、命名


(二)命名规范

代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。正确的英文拼写和语法可以让阅读者易于理解,避免歧义。
*注意:即使纯拼音命名方式也要避免采用。但alibab、taobao、youku、hangzhou等国际通用的名称,可视同英文.

大驼峰规则:每个单词的首字母大写。例:NameTextField。
小驼峰原则:第一个单词首字母小写,其余都大写。例:nameTextField。

1.项目命名

项目名都遵循大驼峰命名。例如:QuickPushCRM

2.类名

类的命名都遵循大驼峰命名。一般是:前缀 + 功能 + 类型。例如:CRM + Login + ViewController

在实际开发中,一般都会给工程中所有的类加上属于本工程的前缀。

常用控件类命名类型对照表(下表中前缀为:CRM,如果用到下表中没有列举出来,请去掉UI首字母,遵循实际规则即可。)

控件名 类型 示例
UIViewController ViewController CRMBaseViewController
UView View CRMBaseView
UITableView TableView CRMOrderTableView
UITableViewCell Cell CRMOrderListCell
UIButton Button CRMSuccessButton
UILabel Label CRMSuccessLabel
UIImageView ImgView CRMGoodsImgView
UITextField TextField CRMNameTextField
UITextView TextView CRMSuggestTextView

其它类相关对照表

功能 类型 示例
工具类 Tool CRMOrderTool
代理类 Delegate CRMOrderListDelegate
管理类 Manager CRMOrderListModel
模型类 Model CRMOrderListModel
Service类 Service CRMOrderService
布局类 Layout CRMHomeLayout
数据库类 DataBase、表名+DBHelper CRMFriendDataBase、CRMUserTableDBHelper
类目 XXX+(范围,例如Extension, Additions 或者功能,例如Frame,Nib,Block) CRMUIButton+Additions、CRMUIButton+Block

3. 方法命名规范

  • 所有方法名称禁止以new开始。

  • 所有方法名称禁止使用_开始。(系统会使用_开头命名一些系统私有方法)

  • 内部私有方法需要增加前缀,前缀需要保持唯一性(例如p_)。

Tips: 给私有方法加前缀有2个好处:

  1. 增加辨识度,提高代码可读性。
  2. 避免自己的方法无意间覆盖了系统/框架同名的私有方法。
  • 如果方法返回接收者的某个属性值,那么请直接使用属性名作为方法名。
正例:
- (CGSize)cellSize;
反例:
- (CGSize)getCellSize;
  • 如果方法间接返回一个或多个值,那么请用"getXXX"命名,这种命名只适用于返回多个数据项的情况。
正例:
- (void)getCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask 
                   completionHandler:(void (^) (NSCachedURLResponse * __nullable cachedResponse))completionHandler;
  • 方法的每个参数前都必须添加关键字。
正例:
- (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag;
反例:
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
  • 参数之前的单词尽量能描述参数的意义。
正例:
- (id)viewWithTag:(NSInteger)aTag;
反例:
- (id)taggedView:(int)aTag;
  • 请不要使用“and”连接接收者属性,尽管and读起来还算顺口,但随着你创建的方法参数的增加,这将会带来一系列的问题。
正例:
- (int)runModalForDirectory:(NSString *)path file:(NSString *) name types:(NSArray *)fileTypes;
反例:
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;
  • 如果方法描述了两个独立的动作,则可以使用"and"连接起来。
正例:
- (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;

4. Protocol命名规范

  • Protocol中的方法命名以触发消息的对象名开头,省略类名前缀并首字母小写,如果它没有关联任何类则可以忽略这个规则。
正例:
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
- (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
  • 除非Protocol方法只有一个参数,否则冒号需紧跟在类名后面。
正例:
- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row;
- (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;

5. Category命名规范

  • 分类命名也要和类命名一样添加前缀。
正例:
UIView (YYAdd)
反例:
UIView (Add)
  • 分类中声明的方法名都要加上前缀。

  • Category中尽量不要声明属性,能挪尽量挪到主类中声明。

Tips: 尽管从技术上来讲可以在分类中声明属性,但是这么做需要格外小心,
因为它很容易出现内存上或其他一些问题,而且一旦出现问题很难排查。

  • 如果一个类比较复杂,那么建议使用分类组织代码(可以参考系统的UIView)。

6. Notification命名规范

  • Notification的命名风格由"类名前缀" + “通知事件名称” + "Notification"3个部分组成。
正例:
UIApplicationDidBecomeActiveNotification
UIApplication表示该通知属于谁,DidBecomeActive表示该通知的作用,Notification表示它是一个通知。
  • 如果一个类声明了delegate属性,通常情况下,这个类的delegate对象应该可以通过实现的delegate方法收到大部分通知消息。

Tips:
例如applicationDidBecomeActive:代理方法和NSApplicationDidBecomeActiveNotification通知(这其实也符合命名规范的基本原则"一致性")。

7. 常量命名规范

  • 如果常量局限于某"编译单元"之内,通常在前面加小写字母k作为前缀,若常量在全局可见,通常以类名作为前缀,然后采用首字母大写的驼峰式命令风格。
正例:
// 局部可见
const CGFloat kAnimationDuration = 2.0;
// 全局可见
const CGFloat UIActivityIndicatorViewAnimationDuration = 2.0;

8.变量和方法

变量和方法的命名都遵循小驼峰命名。例如:textVariableStr, - (void)textAction响应事件。

方法命名对照表(方法多为动词或动名词)

功能 示例
- (id)initXX 初始化相关方法,使用init为前缀标识,如初始化布局- (id)initView
- (BOOL)isXX 方法返回值为boolean型的请使用is前缀标识
- (UIView *)getXX 返回某个值的方法,使用get为前缀标识
- (void)setXX 设置某个属性值或者相关数据
- (void)updateXX 更新数据
- (void)saveXX 保存数据
- (void)drawXX 绘制相关,使用draw前缀标识
- (void)clearXX 清除数据
- (void)XXXAction 响应事件,使用Action为后缀标识
- (void)loadData 加载数据(一般情况下VC中都会有这个方法)
- (void)loadMoreData 加载更多数据
- (void)setupUI 加载布局(一般情况下VC中都会有这个方法)

9. 资源文件命名

全部小写,采用下划线命名法,加前缀区分。所有的资源文件都需要加上工程前缀(小写形式)
命名模式:可加后缀_small表示小图,_big表示大图,逻辑名称可由多个单词加下划线组成,采用以下规则:
用途_模块名_逻辑名称
用途_模块名_颜色
用途_逻辑名称
用途_颜色

说明 前缀(工程前缀示例CRM) 示例
按钮相关 crm_btn_ crm_btn_home_normal、crm_btn_red,crm_btn_red_big
背景相关 crm_btn_ crm_bg_home_header、crm_bg_main
图标相关 crm_ic_ crm_ic_home_location、crm_bg_input
分割线相关 crm_div_ crm_ic_home_location、crm_bg_input
默认相关 crm_def_ crm_ic_home_location、crm_bg_input

10. 文件夹命名

创建文件夹最好创建实体文件夹,找到工程目录,创建相应文件夹并拖入工程。文件夹命名使用相应模块结构分层的英文,首字母要大写。例:ModelViewControllerToolOtherService等等。


(三)编码规范

  • 所有的方法之间空一行。
  • 所有的代码块之间空一行,删除多余的注释。
  • 局部使用的常量、静态变量声明在@interface之前。
  • @property同一类型的声明放在一块,不同类型的声明用2行空格隔开。
正例:
@interface MineViewController ()

@property (nonatomic, weak) UIView *headView;

@property (nonatomic, weak) UITableView *tableView;

我是换行符,请忽略
@property (nonatomic, copy) NSArray *dataSourceArray;
  • 不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开以提升可读性。
正例:
[self createSubviews];
[self createTableview];

[self netRequest];
  • 所有自定义的方法需要给出注释。
  • 尽量使用懒加载,在控制器分类时有提及和要求,其它自定义类按照控制器格式分类,没有的分类不写即可。
  • 代码后的’{‘不需要独占一行,包括方法之后,ifswitch等。
  • 如果有使用到CF(Core Foundation)等框架时,或者是在iOS10以下系统使用通知和KVO时,切记在dealloc方法中释放对象以及移除通知和监听。
  • dealloc方法内禁止将self传递出去,如果selfretain,到下个runloop周期再释放则会多次释放导致crash
反例:
- (void)dealloc {
    [self unsafeMethod:self];
}
  • 当方法可能会提前return时,需要要注意对象的释放问题,避免内存泄漏。
反例:
CFArrayRef arrayRef = (__bridge CFArrayRef)array;

if (x == YES) return;

CFRelease(arrayRef);

以上代码如果x等于YES的话那么arrayRef对象就会内存泄漏。
  • 当使用@try处理异常时,需要要注意对象的释放问题,避免内存泄漏。
反例:
@try {
CFArrayRef arrayRef = (__bridge CFArrayRef)array;

do some thing……

CFRelease(arrayRef);

} @catch (NSException *exception) {

}

以上代码如果do some thing……出现异常的话那么arrayRef就会出现内存泄漏。
  • 必须要统一的要求,属性的定义请按照下图property之后,空一格,括号之后空一格,写上类名,空一格之后跟上*和属性名。
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) DeliveryModel *delivery;
@property (nonatomic, strong) DeliveryLookAdapter *lookAdapter;
@property (nonatomic, strong) DeliveryLookAPIManager *lookManager;

  • 使用一目运算符时左右两边不能有空格。
正例:
i++、++i、
反例:
i ++、++ i
  • 使用二目、三目运算符时左右两边必须有且仅有一个空格。
正例:
1 + 2
反例:
1+2
  • 采用4个空格缩进,如果要使用Tab字符,请将1个Tab设置成4个空格。
  • 单行字符数限制不超过150个,超出需要换行(空格可以除外),换行时遵循如下原则:
Tips:
1. 第二行相对第一行缩进4个空格,从第三行起不再继续缩进。
2. 运算符与下文一起换行。
3. 方法调用的点符号与下文一起换行。

正例:
- (void)setImageWithURL:(nullable NSURL *)imageURL
            placeholder:(nullable UIImage *)placeholder
                options:(YYWebImageOptions)options
               progress:(nullable YYWebImageProgressBlock)progress
               ransform:(nullable YYWebImageTransformBlock)transform
             completion:(nullable YYWebImageCompletionBlock)completion;

  • 遵循一般代码规范,多模仿苹果API
  • 删除不用的代码。
  • 如果有方法一直不会用到,请删除(除工具类)。
  • 没有执行任何业务逻辑的方法,请删除或给予注释,删除多余的资。源或文件,添加必要的注释。
  • 比较大的代码块需要给出注释。
UIViewController请按照如下分类
#pragma mark - Life Cycle
#pragma mark - Private
#pragma mark - Public
#pragma mark - UITableViewDelegate && UITableViewDataSource
#pragma mark - CustomDelegate
#pragma mark - Getters and Setters
#pragma mark - Lazy Loading

注意:所有视图或者对象的创建请尽量使用懒加载,调用的时候全部使用self.textBtn这样的方式。如果是确定的不可变数组、字典,可直接给定数组中的元素。(getters and setters分类中,懒加载可出现_调用对象,其它情况请遵循self.调用原则)

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong)UIButton * textBtn;
@end

@implementation ViewController

#pragma mark - life cycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.textBtn];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
#pragma mark - private 

#pragma mark - event response

#pragma mark - UITableViewDelegate && UITableViewDataSource
//(代理顺序往下排列)

#pragma mark - CTAPIManagerCallBackDelegate

#pragma mark - getters and setters
- (UIButton *)textBtn
{
    if (_textBtn == nil) {
        _textBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        _textBtn.frame = CGRectMake(300, 250, 100, 100);
        _textBtn.backgroundColor = [UIColor yellowColor];
        _textBtn.titleLabel.text = @"text";
        [_textBtn addTarget:self action:@selector(text) forControlEvents:UIControlEventTouchUpInside];
    }
    return _textBtn;
}
#pragma mark - Lazy Loading

@end


(四) 注释规范

为了减少他人阅读你代码的痛苦值,请在关键地方做好注释。

1.类注释

//
//  CRMNewToNetViewController.h
//  QuickPushCRM
//
//  Created by susu on 2021/6/18.
//  Copyright © 2021 方俊. All rights reserved.
//

该注释是自动生成的,在xcode中设置即可。Created by 电脑用户名on 创建该文件的时间。Copyright 2021 后面的名字和邮箱是自己填写和设置的。具体可在xcode工程,Project Document中设置。这样便可在每次新建类的时候自动加上该头注释。

2.方法注释

方法注释,方法外部统一用option + command + /,方法内部统一用//注释。

/**
 测试
 */
- (void)text
{
    //测试按钮事件响应
}

3.模型注释

每个model中的,包含的每个属性,都必须要写上相对应的注释,用///注释。阅读者一看这个model,就清楚知道model中的每个字段代表的意思,用来做什么事情的。

@interface DeliveryModel : NSObject
///提货劵所在商圈id
@property (nonatomic, assign) long long mallId;
///商圈全称
@property (nonatomic, copy) NSString *mallFullName;
///商圈简称
@property (nonatomic, copy) NSString *mallShortName;
///提货劵号
@property (nonatomic, copy) NSString *credentialsCode;
///总金额
@property (nonatomic, assign) NSInteger totalAmount;
///提货劵所在店铺id
@property (nonatomic, assign) long long storeId;
///货劵所在店铺名称
@property (nonatomic, copy) NSString *storeName;
///提货劵id
@property (nonatomic, strong) NSNumber *credentialsId;
///状态:0:未提货、1:已提货、2:已分享、3:已退款
@property (nonatomic, assign) NSInteger state;
///提货商品(以下为提货商品参数)
@property (nonatomic, strong) NSArray *goodsList;
///二维码
@property (nonatomic, copy) NSString *qrCode;
///商品总个数
@property (nonatomic, assign) NSInteger goodsCount;
@end

如果不是model的属性,是其它类属性,需要注释,请按照model属性注释方式。

  • 对于代码注释需谨慎,代码被注释一般有2种可能,1) 后续会恢复此段代码逻辑; 2) 永久不用;对于第1种情况需添加相应注释,如果没有注释信息难以知晓注释动机,后者建议直接删除。如果有需要可以通过代码仓库查阅历史代码。

  • 使用特殊注释标记时,请注明标记人和标记时间,注意及时处理这些标记。

正例:
/**
* @brief 简要描述
* @author 标明开发该类模块的作者
*/
// FIXME: 有bug,需要修改
- (void)testFunction;

(五) 版本规范

采用A.B.C 三位数字命名,比如:1.0.2,当有版本更新的时候,依据下面的情况来确定版本号规范。

版本号 说明 示例
A.b.c 属于重大更新内容 1.0.2 -> 2.00
a.B.c 属于小部分更新内容 1.0.2 -> 1.2.2
a.b.C 属于补丁更新内容 1.0.2 -> 1.0.4

(六)第三方库规范

希望Team能用时下较新的技术,对开源库的选取,一般都需要选择比较稳定的版本,作者在维护的项目,要考虑作者对issue的解决,以及开发者的知名度等各方面。选取之后,一定的封装是必要的。

项目使用cocoapods统一管理开源第三库文件,不需要手动导入和手动添加依赖库。如果第三方不支持cocoapods,可手动导入工程。


(七)其它规范

  • 提取方法,去除重复代码。对于必要的工具类抽取也很重要,这在以后的项目中是可以重用的。
  • 尽可能的使用局部变量
  • 尽量减少对变量的重复计算。
  • 尽量在合适的场合使用单例。使用单例可以减轻加载的负担,缩短加载的时间,提高加载效率。但并不是所有的地方都适用于单例,简单来说单例主要适用于以下三个方面:
    1. 控制资源的使用,通过线程同步来控制资源的并发访问。
    2. 控制实例的产生,以达到节约资源的目的。
    3. 控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信。
  • 最后不要忘了检测内存泄漏。可使用Instruments分析内存。

你可能感兴趣的:(iOS开发规范)