声明:
- 本文档面向以Objective-C作为开发语言的iOS团队。
- 本文档以苹果开发文档为基础,结合网络内容和开发实践进行整理,针对iOS开发进行规范和约定。
1.项目基础
1.1 项目新建信息
- Product Name:工程名。
- Team:开发者账号信息,没有选择None,个人开发者账号(含Personal Team),公司/企业账号(如:XXX Co.,LTD),也可以暂时选择None,后续再配置调试。
- Organization Name:个人开发者账号(自定义名字),公司/企业账号(如:XXX Co.,LTD),此处内容明显体现在代码文件头部注释中。
- Organization Identifier:个人开发者账号(自定义标识),公司/企业账号(域名反写如:com.xxx)。
- Bundle Identifier:自动生成格式为 [Organization Identifier] + [Product Name],也可后续配置时进行修改。
- Language:根据需要选择Objective-C 或者 Swift。
- Use Core Data:根据项目情况勾选,如果明确需要请直接勾选,也可后续添加。
- Include Unit Tests:单元测试,根据需要勾选,也可后续添加。
- Include UI Tests:UI测试,根据需要勾选,也可后续添加。
- Source Control:默认git进行版本管理,根据需要勾选,也可后续添加。
1.2 项目初始配置
- Display Name:应用名称。
- Deployment Info:系统版本、设备、屏幕方向、状态栏等,
- Build Active Architecture Only:一般Debug模式YES,Release模式NO。如果Release模式为YES,那么上传AppStore之后会显示大量而具体的兼容设备,Release模式为NO则仅显示模糊的兼容信息。
- Architecture:默认$(ARCHS_STANDARD)不作修改。
- Valid Architecture:默认arm64/armv7/armv7s不作修改。模拟器32位处理器是i386架构,模拟器64位处理器是x86_64架构,真机32位处理器是armv7或armv7s架构,真机64位处理器是arm64架构。
1.3 项目文件结构
- 所有的文件应放在工程中的项目目录下。
- 项目文件和物理文件需保持一致。
- Xcode创建的任何组(group)都必须有文件夹映射。
- 项目文件不仅可以按照业务类型分组,也可以根据功能分组。
2.代码格式规范
2.1 代码注释格式
文件注释:采用Xcode自动生成的注释格式。
// // AppDelegate.h // 项目名称 // // Created by 开发者姓名 on 2018/6/8. // Copyright © 2018年 公司名称. All rights reserved. //
import注释:如果有一个以上的import语句,对这些语句进行分组,每个分组的注释是可选的。
// Framework #import <UIKit/UIKit.h> // Model #import "WTUser.h" // View #import "WTView.h"
方法注释:Xcode8之后快捷键自动生成(option + command + /)。
/** <#Description#> @param application <#application description#> @param launchOptions <#launchOptions description#> @return <#return value description#> */ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
- 代码块注释:单行的用 “// + 空格” 开头, 多行用“/* */”。
2.2 代码结构与排版
- 声明文件:方法顺序和实现文件的顺序保持一致,根据需要用”#pragma mark -“将方法分组。
实现文件:必须用”#pragma mark -“将方法分组。分组前后优先级:Lifecycle方法 > Public方法 > UI方法 > Data方法 > Event方法 > Private方法(逻辑处理等) > Delegate方法 > 部分Override方法 > Setter方法 > Getter方法。
#pragma mark - Lifecycle - (instancetype)init {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)viewDidAppear:(BOOL)animated {} - (void)viewWillDisappear:(BOOL)animated {} - (void)viewDidDisappear:(BOOL)animated {} - (void)didReceiveMemoryWarning {} - (void)dealloc {} #pragma mark - Public - (void)refreshData {} #pragma mark - UI - (void)initSubViews {} #pragma mark - Data - (void)initData {} - (void)constructData {} #pragma mark - Event - (void)clickButton:(UIButton *)button {} #pragma mark - Private - (CGFloat)calculateHeight {} #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView {} #pragma mark - Override - (BOOL)needNavigationBar {} #pragma mark - Setter - (void)setWindow:(UIWindow *)window {} #pragma mark - Getter - (UIWindow *)window {}
变量:优先使用属性声明而非变量声明,注意属性修饰符、变量类型、变量之间的间隔。
@property (strong, nonatomic) UIWindow *window;
- 点语法:应始终使用点语法来访问和修改属性。
间距要求如下:
- 一个缩进使用四个空格。
在”-“或者”+“号之后应该有一个空格,方法的大括号和其它大括号始终和声明在同一行开始,在新的一行结束,另外方法之间应该空一行。
(BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {
if (door.isClosed) {// Do something
} else {
// Do something
}
return YES;
}
长度要求如下:
- 每行代码的长度不应该超过100个字符。
- 单个函数或方法的实现代码控制在50行以内。
- 单个文件里的代码行数控制在500~600行之内。
3.代码命名规范
3.1 代码命名基础
- 最好是既清晰又简短,但不要为简短丧失清晰性,并使用驼峰命名法。
名称通常不缩写,即使名称很长也要拼写完全(禁止拼音),然而可使用少数非常常见的缩写,部分举例如下:
常用缩写词 含义 常用缩写词 含义 app application max maximum alt alternate min minimum calc calculate msg message alloc allocte rect rectangle dealloc deallocte msg message init initialize temp temporary int integer func function - 由于Cocoa(Objective-C)没有C++一样的命名空间机制,需添加前缀(公司名首字母)防止命名冲突,前缀使用2个字符(以下统称项目前缀)。
- 常见的单词略写:ASCII,PDF,HTTP,XML,URL,JPG,GIF,PNG,RGB等
3.2 类和协议命名
- 类名应明确该类的功能,并且要有项目前缀防止命名冲突。
- 协议组合一组相关的方法,不关联具体的一个类,使得某些相似类有统一的接口,这种协议的命名应采用动名词形式(ing),例如:NSLocking。
- 协议组合一些不相关的方法(主要是避免创建多个独立的协议),仅仅关联某一个具体的类(该类是协议的具体体现者),这种协议用该类名命名,例如:NSObject。
- 委托形式的协议命名为类名加上Delegate,例如:UIScrollViewDelegate。
3.3 变量和属性命名
- 变量名应前置下划线“_”,属性名没有下划线。
属性本质上是存取方法setter/getter,可进行重写(注意内存管理)。
@property (strong, nonatomic) UIWindow *window; - (void)setWindow:(UIWindow *)window; - (UIWindow *)window;
可以适当的对setter/getter进行别名设置。
@property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteractionEnabled;
3.4 方法和函数命名
- 方法名和函数名一般不需要前缀,但函数(C语言形式)作为全局作用域的时候最好加上项目前缀。
- 表示行为的方法名称以动词开头,但不要使用do/does等无实际意义的助动词。
参数前面的单词要能够描述该参数,并且参数名最好能用描述该参数的单词命名。
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
方法中多个参数可以使用适当的介词进行连接。
// 后续多个参数使用with - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; // 添加适当介词能够使方法的含义更明确 - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit; // 第一个参数用了with,后面的参数不使用with - (instancetype)initWithImage:(nullable UIImage *)image highlightedImage:(nullable UIImage *)highlightedImage;
只有在方法返回多个值的时候使用get单词进行明确。
- (void)getLineDash:(nullable CGFloat *)pattern count:(nullable NSInteger *)count phase:(nullable CGFloat *)phase;
方法返回某个对象实例。
+ (instancetype)buttonWithType:(UIButtonType)buttonType;// 类方法创建对象 + (UIApplication *)sharedApplication;// 单例命名
委托或代理方法命名第一个参数最好能相关某个对象。
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
- 私有方法不要以下划线“_“开头,因为系统私有方法保留此方式。
- 自定义方法和系统方法重名,建议在方法开头加前缀”xx_methodName“。
3.5 常量和宏的命名
- const常量外部声明:在Objective-C文件中优先采用FOUNDATION_EXTERN和UIKIT_EXTERN,而非C语言中的extern。
- const常量采用驼峰命名原则。
- const常量根据作用域适当加上前缀(含项目前缀):可供外部使用需加上相应的类名或者模块前缀,仅文件内部使用需要加上小写字母“k”.
- 宏定义每个字母采用大写,单词之间用下划线“_”间隔。
- 宏定义也可根据作用范围加上适当前缀,避免命名冲突。
3.6 枚举的命名
- 使用枚举来定义一组相关的整数常量,增强代码的可读性。
- 枚举可根据作用域添加前缀(含项目前缀),格式:[相关类名或功能模块名] + [描述] + [状态]。
- 建议优先采用Objective-C的声明NS_ENUM和NS_OPTIONS,少采用C语言形式的enum等枚举声明.
枚举定义时需指定None状态,并且其rawValue一般为起始值0。
// NS_ENUM typedef NS_ENUM(NSInteger, UIStatusBarAnimation) { UIStatusBarAnimationNone = 0, UIStatusBarAnimationFade = 1, UIStatusBarAnimationSlide = 2, } typedef NS_OPTIONS(NSUInteger, UIRemoteNotificationType) { UIRemoteNotificationTypeNone = 0, UIRemoteNotificationTypeBadge = 1 << 0, UIRemoteNotificationTypeSound = 1 << 1, UIRemoteNotificationTypeAlert = 1 << 2, UIRemoteNotificationTypeNewsstandContentAvailability = 1 << 3, }
3.7 通知命名
- 外部声明:在Objective-C文件中优先采用FOUNDATION_EXTERN和UIKIT_EXTERN,而非C语言中的extern。
通知的命名一般都是跨文件使用的,需添加项目前缀。
// [相关联类名或者功能模块名] + [will/Did](可选) + [描述] + Notification UIApplicationDidEnterBackgroundNotification UIApplicationWillEnterForegroundNotification
3.8 类型别名命名
- 根据作用域添加前缀(含项目前缀),格式:[类名或功能模块名] + [描述]。
4.文件资源命名规范
- 资源文件命名也需加上项目前缀。
- 资源文件名全小写,单词之间用下划线“_”间隔。
- 资源文件命名格式:[项目前缀] + [业务] + [文件名]
图片文件命名格式:[项目前缀] + [业务] + [类型] + [状态]。
// 常见类型:logol,icon,img // 常见状态:normal,selected,highlight UIImage *image = [UIImage imageNamed:@"wt_mine_setting_normal"];
5.代码警告处理
- 注意警告问题的隐蔽性,因此最好修复警告。
- 警告类型的查看步骤:选中警告 -> 右键Reveal in Log(不编译Reveal in Log是灰色的,因此先编译) ->查看方括号的内容
如果需要忽略警告,建议优先代码push或者pop处理。
#pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" // 造成警告的代码 #pragma clang diagnostic pop
如果警告数量过大,检查警告类型以及必要性,可xcode配置忽略此类型警告。步骤:选中工程 -> TARGETS -> Build Settings -> Other Warning Flags。
忽略单个和全局配置稍有差别,如下举例: push/pop Other Warning Flags -Wformat —-> -Wno-format -Wunused-variable —-> -Wno-unused-variable -Wundeclared-selector —-> -Wno-undeclared-selector -Wint-conversion —-> -Wno-int-conversion
- 也可以在pch等大范围作用域的头文件中添加代码来忽略后续警告:#pragma clang diagnostic ignored “警告名称” 。
6.外部库文件引入
- 库文件引入最好把警告处理掉。
- 库文件引入优先采用CocoaPods引入,并且指定版本号。
- 源文件方式需引入文件到工程目录下。
- 源文件方式需注意有无版本说明信息(可能在README文件中,也可能在某个.h头文件中,又或者有Version文件)没有时需在库文件目录下新增版本说明文件,
7.代码版本管理
- 版本管理工具:svn 或 git。
- svn文件管理配置:目录~/.subversion打开config文件全局配置global-ignore,所有的仓库都会受到影响,而svn:ignore只影响仓库目录。
git文件管理配置:.gitignore_global为全局配置,而仓库目录下的.gitignore文件仅注明本仓库被git忽略的文件,常见语法如下:
- 井号(#)用来添加注释用的,比如 "#注释"。
- build/ : 星号()是通配符,build/*则是要说明要忽略 build 文件夹下的所有内容。
- *.pbxuser : 表示要忽略后缀名为.pbxuser的文件。
- !default.pbxuser : 感叹号(!)是取反的意思,*.pbxuser 表示忽略所有后缀名为.pbxuser的文件,如果加上!default.pbxuser则表示,除了default.pbxuse忽略其它后缀名为pbxuse的文件。
提交信息规范:
- BUG类型为“Fix + [BUG编号] + [BUG描述]”。
- 任务类型为“Done + [任务编号] + [任务描述]”。
- 任务中间态为“Doing + [任务编号] + [任务描述]”。
- 引入类库为“import + [类库名]”。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。