该文档规范了iOS编码时的相关注意事项,iOS开发人员必须认真阅读、理解和严格遵守该规范。其目的在于促进开发团队的代码规范化和成员的编码风格,同时提高团队的整体代码质量,降低后期代码维护成本等。
编码规范总体要求:
- 切图在有2倍图和3倍图时,不允许再添加1倍图。
- Git提交、合并到主分支必须要有详细备注说明,最终合并代码中不允许出现测试代码。
- 所属开发模块不允许再出现编译警告。
- 新界面开发必须采用自动布局(xib或者Masonry)。
- Git建议一个需求一个分支,一个Bug一个分支。
- 分支合并,当APP全局配置存在冲突时,全部以主分支为准。如有变更请详细说明原因。
- 业务参数和业务逻辑必须添加相应注释。无相关注释禁止合并代码。
- 所负责业务需求,必须进行自测和兼容性测试。
- 及时删除不需要的资源文件和类文件。
- 不仅要实现功能,更要时刻关注性能。
编码命名规范:
要求 | 示例 | |
目录/文件夹 | 1.目录文件夹名称单词首字母大写,不可使用中文。 2. Xcode目录引用:创建物理目录,添加引用,不直接创建虚拟目录。 3.目录划分按照功能模块或者职责。 4.新建文件夹注意target选择 | Hotel Controller ViewModel |
类 | 1.类名单词首字母大写,缩写词字母全部大写,遵循驼峰法命名。 2.视图控制器的子类应该添加后缀“ViewController”或者“Controller”,视图的子类则应该添加具体视图的后缀,XxxView,XxxButton,XxxLabel 3.不同业务模块应选取合适的前缀,避免命名出现冲突。 4.尽量使用能够反映类功能的名词短语 5.新建类请注意target选择。 | AppDelegate.h URLController.h HotelHomeViewController.h HotelHomeTableView.h SettingView SettingButton |
Category | 1.Category命名必须为:类名+name.h 2.通过分类名称可知道该Category的功能 3.Category中的自定义方法尽量添加前缀,避免方法覆盖,例如sd_前缀 | NSString+MD5.h – (nullable NSURL *)sd_currentImageURL; |
方法 | 1.方法使用小写开头的驼峰法命名,每个参数都应该小写开头, 参数类型星号前要有一个空格。- 或者 + 符号后面紧跟一个空格。 2. 减少方法参数个数,如方法参数过多,建议采用Model或集合进行替代。 3.方法名尽量使用能够表明方法目的的动词短语 4.方法参数首字母小写,之后每个单词字母大写 5.方法应添加合理注释 6.方法参数过长,建议换行并以:对齐 | +(NSURL *)URLWithString:(NSString *)URLString; – (void)sd_setImageWithURL:(nullable NSURL *)url forState:(UIControlState)state completed:(nullable SDExternalCompletionBlock)completedBlock; |
属性 | 1.属性声明采用nonatomic,为第一个关键字 2.UIKit相关属性声明,须添加具体后缀。例如:historyTableView,deleteOrderButton,nameLabel 3.只读属性添加readonly关键字 4.尽量使用Code Snippet快捷生成 5.小写开头,驼峰命名 6.属性类型与服务器返回类型保持一致。例如:int对应int,string对应NSString | @property (nonatomic, strong, nullable) UIView *backgroundView |
变量 | 1.小写字母开头,驼峰命名。 2.添加类型后缀,xxxView,xxxButton | UIView *backgroundView; |
常量 | 1.全局宏定义建议添加模块前缀,避免冲突 2.全局常量使用小写k开头的驼峰法 3.局部常量建议添加当前类名为前缀 | |
protocol delegate | 1.@protocol名称须以 类名+Delegate/DataSource.h 2.delegate属性声明使用weak关键字 3.合理使用@optional 和@required | @protocol UITableViewDelegate<NSObject, UIScrollViewDelegate> @property (nonatomic, weak, nullable) id <UITableViewDelegate> delegate; |
Block | 1.尽量采用typedef定义block 2.属性中使用copy关键字 3.block须检查是否存在循环引用 4.block中的代码逻辑不宜过长,须考虑是否单独抽离方法 5.block回调时应检查是否为空 | |
枚举 | 1.统一采用OC风格枚举 2.枚举项命名按照枚举名称+xxx形式 3.枚举判断优先采用switch 4.采用NS_ENUM和NS_OPTIONS定义枚举,并指明数据类型。 | typedef NS_ENUM(NSInteger, UIViewTintAdjustmentMode) { UIViewTintAdjustmentModeAutomatic, UIViewTintAdjustmentModeNormal, UIViewTintAdjustmentModeDimmed, } |
通知名称 | 1.通知名称以小写k开头,Notification结尾 2.通知名称要加具体业务模块名称进行区分,防止通知名称冲突 3.添加具体的注释说明通知发出时机 4.尽量采用全局定义 | kHotelOrderUpdateNotification |
图片 | 1.采用Assets管理 2.图片目录按照功能模块进行划分 3.名称按照 类型+模块+name或模块+name 4.图片区分target,智能公务之家单独Assets | icon_hotel_arrow |
日志 | 统一采用日志中心进行日志打印和日志上报。不允许出现无用的日志。 |
注释规范:
要求 | 示例 | |
类文件注释 | 1.类文件Xcode会自动添加相关注释 2.作者请使用开发人员姓名全拼或缩写 | Created by awnlab on 2019/1/15. |
方法注释 | 1.使用Xcode 注释方法,option+command+/ 2.方法功能说明,参数说明,返回值说明 3.避免不必要的注释 | /** 重置badge number为0 */ +(void)resetAppBadgeNumber; |
属性/变量注释 | 单行注释采用//或///方式添加注释 多行注释采用/* xxx */ /** Xxxxxx */ 业务Model属性必须添加注释,说明字段含义用途等。重要! | @property(nonatomic) CGPoint center; // center is center of frame. animatable |
#pragma mark | 采用pragma mark进行代码功能分块注释。 | #pragma mark – JPush Delegate |
//TODO: | 尚未完成的逻辑代码建议添加TODO注释,完成之后删除该注释,不允许出现已完成功能后仍存在TODO注释的情况。重要! | //TODO: xxxxxx |
NSAssert | 公共API或公共组件建议合理使用断言。 | |
Xcode编译警告 | 所负责模块代码不允许出现Xcode警告,出现警告后应当认真检查是否存在问题,并解决警告。 | |
测试代码 | 最终合并代码不允许存在测试代码。 | |
单元测试 | 编写合理可用的单元测试。 | |
git代码提交与合并 | 每次代码提交、合并到主分支,必须有详细的开发备注描述。 |
第三方库:
- 尽量减少第三方库的使用,框架导入直接导入根头文件。
- 非通用工具类第三方库,禁止在PCH或公共头文件中进行引用,应当由具体业务模块自己引用。
- 第三方库优先采用CocoaPods管理。
- 核心业务代码中建议尽量减少对第三方库的直接使用。
- CocoaPods添加新的第三方库后建议使用pod install命令,禁止使用pod update命令,如需更新某个第三库到最新版本,请使用pod update xxxx(注意提前考虑更新后是否对项目代码产生影响)。
- 如需导入全局第三方工具库或者替换系统方法,请提前考虑和检查该库所产生的影响,评估完成后方可导入。例如:全局返回手势添加、全局键盘管理等。
- 多Target项目,新添加第三方库,请确认所属Target。
间距和格式:
方法声明和定义:应在-或+与返回类型之间使用一个空格,参数类型与*之间建议使用一个空格。如果方法名称较长,且存在多个参数时,建议除一行外,其它参数都应保持单独的一行,且以:对齐。如无法与第一个参数:对齐,则应优先保证其他参数以:对齐。
条件语句:在if,for,while,switch后使用一个空格,并在比较运算符前后使用一个空格。当if存在else时,则应都使用{}
方法调用:确保所有参数在一行或者每个参数一行并以:对齐。当与第一个参数无法对齐时,优先保证后续参数:对齐。
Switch:每个分支建议都必须用大括号括起来,并添加default分支处理。
Objective-C特性:
重载生命周期函数:子类控制器中生命周期相关函数,一定要调用父类函数(如不调用须添加注释说明)。例如:[super viewDidLoad];
重载指定构造函数:当在子类中,如果需要init…方法,必须重载父类指定的构造函数。
精简initialize与load的实现代码:避免实现一些复杂且耗时的逻辑。
保持公共API简单:保持公共API简单,避免包含一切功能的API,遵守单一职责。
import 与 #include:使用#import来引用Objective-C/Objective-C++头文件,使用#include引用C/C++头文件。可使用前向声明@class时,优先前向声明。
使用根框架:系统框架应包含根框架,而非单独的文件。
字符串应使用copy关键字:应总是用 copy 属性关键字声明 NSString 属性。
尽量多用类型常量,少用宏定义:尽量不要使用预处理指令定义常量,这样定义出来的常量不含类型信息,编译器只是会在编译前进行查找和替换操作。如果有人重新定义了常量值,这将可能导致程序出现错误。在实现文件中可使用static const定义“只在编译单元内可见的常量”。在头文件中可使用extern或UIKIT_EXTERN来定义全局常量,并在实现文件中定义其值。
代理使用:代理声明应总使用weak修饰,在向代理发送消息的时候,需要判断委托者是否实现了这个代理方法。代理方法声明应合理使用@optional 和@required。
没有实例变量的接口:没有有声明任何实例变量的接口,应省略空大括号。
循环引用:应总是检查并关注VC页面是否正常释放。并在易产生循环引用的地方进行检查。例如block、NSTimer、delegate、子view强引用vc等情况。
单例:单例名称建议采用sharedXXX/SharedInstance/defaultXXX/defaultInstance,必须使用dispatch_once创建。单例中如有block属性,应注意该block是否强引用了某个VC,导致VC不能正常释放。
类结构:生命周期函数应在最上方,其他代码按功能划分即可,不同功能代码块之间应使用pragma mark进行分割。
示例:
#pragma mark – Lifecycle
– (instancetype)init {}
– (void)viewDidLoad {}
– (void)viewWillAppear:(BOOL)animated {}
– (void)didReceiveMemoryWarning {}
– (void)dealloc {}
#pragma mark – Getter
– (NSString *)name;
#pragma mark – Setter
– (void)setName:(NSString *)name;
#pragma mark – Public Methods
– (void)publicMethod {}
#pragma mark – Private Methods
– (void)privateMethod {}
#pragma mark – Some Delegate
#pragma mark – NSCopying
– (id)copyWithZone:(NSZone *)zone {}
#pragma mark – IBActions
– (IBAction)submitData:(id)sender {}
其他内容:
https://github.com/google/styleguide