姬長信(Redy)

看完这篇你们团队的代码也很规范

> 最近重构项目组件/uff0c看到项目中存在一些命名和方法分块方面存在一些问题/uff0c结合平时经验和 [Apple官方代码规范](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html) 在此整理出 iOS 工程规范。提出第一个版本/uff0c如果后期觉得有不完善的地方/uff0c继续提出来不断完善/uff0c文档在此记录的目的就是为了大家的代码可读性较好/uff0c后来的人或者团队里面的其他人看到代码可以不会因为代码风格和可读性上面造成较大时间的开销。 软件的生命周期贯穿产品的开发/uff0c测试/uff0c生产/uff0c用户使用/uff0c版本升级和后期维护等过程/uff0c只有易读/uff0c易维护的软件代码才具有生命力。 ## 一些原则 1. 长的/uff0c描述性的方法和变量命名是好的。不要使用简写/uff0c除非是一些大家都知道的场景比如 VIP。不要使用 bgView/uff0c推荐使用 backgroundView 2. 见名知意。含义清楚/uff0c做好不加注释代码自我表述能力强。/uff08前提是代码足够规范/uff09 3. 不要过分追求技巧/uff0c降低代码可读性 4. 删除没必要的代码。比如我们新建一个控制器/uff0c里面会有一些不会用到的代码/uff0c或者注释起来的代码/uff0c如果这些代码不需要/uff0c那就删除它/uff0c留着偷懒吗/uff1f下次需要自己手写 5. 在方法内部不要重复计算某个值/uff0c适当的情况下可以将计算结果缓存起来 6. 尽量减少单例的使用。 7. 提供一个统一的数据管理入口/uff0c不管是 MVC、MVVM、MVP 模块内提供一个统一的数据管理入口会使得代码变得更容易管理和维护。 8. 除了 .m 文件中方法/uff0c其他的地方"{"不需要另起一行。 ```Objective-c - (void)getGooodsList { // ... } - (void)doHomework { if (self.hungry) { return; } if (self.thirsty) { return; } if (self.tired) { return; } papapa.then.over; } ``` ## 变量 1. 一个变量最好只有一个作用/uff0c切勿为了节省代码行数/uff0c觉得一个变量可以做多个用途。/uff08单一原则/uff09 2. 方法内部如果有局部变量/uff0c那么局部变量应该靠近在使用的地方/uff0c而不是全部在顶部声明全部的局部变量。 ## 运算符 1. 1元运算符和变量之间不需要空格。例如/uff1a++n 2. 2元运算符与变量之间需要空格隔开。例如/uff1a containerWidth = 0.3 * Screen_Width 3. 当有多个运算符的时候需要使用括号来明确正确的顺序/uff0c可读性较好。例如/uff1a 2 delegate; @property (nonatomic, copy) (^)(); ``` ## 单例 单例适合全局管理状态或者事件的场景。一旦创建/uff0c对象的指针保存在静态区/uff0c单例对象在堆内存中分配的内存空间只有程序销毁的时候才会释放。基于这种特点/uff0c那么我们类似 UIApplication 对象/uff0c需要全局访问唯一一个对象的情况才适合单例/uff0c或者访问频次较高的情况。我们的功能模块的生命周期肯定小于 App 的生命周期/uff0c如果多个单例对象的话/uff0c势必 App 的开销会很大/uff0c糟糕的情况系统会杀死 App。如果觉得非要用单例比较好/uff0c那么注意需要在合适的场合 tearDown 掉。 单例的使用场景概括如下/uff1a - 控制资源的使用/uff0c通过线程同步来控制资源的并发访问。 - 控制实例的产生/uff0c以达到节约资源的目的。 - 控制数据的共享/uff0c在不建立直接关联的条件下/uff0c让多个不相关的进程或线程之间实现通信。 ```objective-c + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //because has rewrited allocWithZone use NULL avoid endless loop lol. _sharedInstance = [[super allocWithZone:NULL] init]; }); return _sharedInstance; } + (id)allocWithZone:(struct _NSZone *)zone { return [TestNSObject sharedInstance]; } + (instancetype)alloc { return [TestNSObject sharedInstance]; } - (id)copy { return self; } - (id)mutableCopy { return self; } - (id)copyWithZone:(struct _NSZone *)zone { return self; } ``` ## 私有变量 推荐以 `_` 开头/uff0c写在 .m 文件中。例如 NSString * _somePrivateVariable ## 代理方法 1. 类的实例必须作为方法的参数之一。 2. 对于一些连续的状态的/uff0c可以加一些 will/uff08将要/uff09、did/uff08已经/uff09 3. 以类的名称开头 ```objective-c - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath; ``` ## 方法 1. 方法与方法之间间隔一行 2. 大量的方法尽量要以组的形式放在一起/uff0c比如生命周期函数、公有方法、私有方法、setter && getter、代理方法.. 3. 方法最后面的括号需要另起一行。遵循 Apple 的规范 4. 对于其他场景的括号/uff0c括号不需要单独换行。比如 if 后面的括号。 5. 如果方法参数过多过长/uff0c建议多行书写。用冒号进行对齐。 6. 一个方法内的代码最好保持在50行以内/uff0c一般经验来看如果一个方法里面的代码行数过多/uff0c代码的阅读体验就很差/uff08别问为什么/uff0c做过重构代码行数很长的人都有类似的心情/uff09 7. 一个函数只做一个事情/uff0c做到单一原则。所有的类、方法设计好后就可以类似搭积木一样实现一个系统。 8. 对于有返回值的函数/uff0c且函数内有分支情况。确保每个分支都有返回值。 9. 函数如果有多个参数/uff0c外部传入的参数需要检验参数的非空、数据类型的合法性/uff0c参数错误做一些措施/uff1a立即返回、断言。 10. 多个函数如果有逻辑重复的代码/uff0c建议将重复的部分抽取出来/uff0c成为独立的函数进行调用 ```objective-c - (instancetype)init { self = [super init]; if (self) { } return self; } - (void)doHomework:(NSString *)name period:(NSInteger)second score:(NSInteger)score; ``` 11. 方法如果有多个参数的情况下需要注意是否需要介词和连词。很多时候在不知道如何抉择测时候思考下苹果的一些 API 的方法命名。 ```objective-c //good - (instancetype)initWithAge:(NSInteger)age name:(NSString *)name; - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; //bad - (instancetype)initWithAge:(NSInteger)age andName:(NSString *)name; - (void)tableView:(UITableView *)tableView :(NSIndexPath *)indexPath; ``` 12. `.m` 文件中的私有方法需要在顶部进行声明 13. 方法组之间也有个顺序问题。 - 在文件最顶部实现属性的声明、私有方法的声明/uff08很多人省去这一步/uff0c问题不大/uff0c但是蛮多第三方的库都写了/uff0c看起来还是会很方便/uff0c建议书写/uff09。 - 在生命周期的方法里面/uff0c比如 viewDidLoad 里面只做界面的添加/uff0c而不是做界面的初始化/uff0c所有的 view 初始化建议放在 getter 里面去做。往往 view 的初始化的代码长度会比较长、且一般会有多个 view 所以 getter 和 setter 一般建议放在最下面/uff0c这样子顶部就可以很清楚的看到代码的主要逻辑。 - 所有button、gestureRecognizer 的响应事件都放在这个区域里面/uff0c不要到处乱放。 文件基本上就是 ```objective-c //___FILEHEADER___ #import "___FILEBASENAME___.h" /*ViewController*/ /*View&&Util*/ /*model*/ /*NetWork InterFace*/ /*Vender*/ @interface ___FILEBASENAMEASIDENTIFIER___ () @end @implementation ___FILEBASENAMEASIDENTIFIER___ #pragma mark - life cycle - (void)viewWillAppear:(BOOL)animated { [super viewDidAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } - (void)viewDidLoad { [super viewDidLoad]; self.title = ; } - (void)viewWillDisappear:(BOOL)animated { [super viewDidAppear:animated]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidAppear:animated]; } #ifdef DEBUG - (void)dealloc { NSLog(@"%s",__func__); } #endif #pragma mark - public Method #pragma mark - private method #pragma mark - event response #pragma mark - UITableViewDelegate #pragma mark - UITableViewDataSource //...(多个代理方法依次往下写) #pragma mark - getters and setters @end ``` ## 图片资源 1. 单个文件的命名 文件资源的命名也需要一定的规范/uff0c形式为/uff1a功能模块名_类别_功能_状态@nx.png Setting_Button_search_selected@2x.png、Setting_Button_search_selected@3x.png Setting_Button_search_unselected@2x.png、Setting_Button_search_unselected@3x.png 2. 资源的文件夹命名 最好也参考 App 按照功能模块建立对应的实体文件夹目录/uff0c最后到对应的目录下添加相应的资源文件。 ## 注释 1. 对于类的注释写在当前类文件的顶部 2. 对于属性的注释需要写在属性后面的地方。 //** 2.0.0 | | a.**B**.c | 属于小部分内容的更新 | 1.0.2 -> 1.1.1 | | a.b.**C** | 属于补丁更新 | 1.0.2 -> 1.0.3 | ## 改进 我们知道了平时在使用 Xcode 开发的过程中使用的系统提供的代码块所在的地址和新建控制器、模型、view等的文件模版的存放文件夹地址后/uff0c我们就可以设想下我们是否可以定制自己团队风格的控制器模版、是否可以打造和维护自己团队的高频使用的代码块/uff1f 答案是可以的。 Xcode 代码块的存放地址/uff1a`~/Library/Developer/Xcode/UserData/CodeSnippets` Xcode 文件模版的存放地址/uff1a/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates/ ## 意义 1. 为了个人或者团队开发者的代码更加规范。Property的书写的时候的空格、线程修饰词、内存修饰词的先后顺序 2. 提供大量可用的代码块/uff0c提高开发效率。比如在 Xcode 里面敲 UITableView_init 便可以自动懒加载创建一个 UITabelView 对象/uff0c你只需要设置在指定的位置写相应的参数 3. 通过一些代码块提高代码规范、避免一些bug。比如曾看到过 block 属性用 strong 修饰的代码/uff0c造成内存泄漏。举个例子你在 Xcode 中输入 **Property_delegate** 就会出来 `@property (nonatomic, weak) id> delegate;`/uff0c你输入 **Property_block** 就会出来 `@property (nonatomic, copy) (^)();` ## 代码块的改造 我们可以将属性、控制器生命周期方法、单例构造一个对象的方法、代理方法、block、GCD、UITableView 懒加载、UITableViewCell 注册、UITableView 代理方法的实现、UICollectionVIew 懒加载、UICollectionVIewCell 注册、UICollectionView 的代理方法实现等等组织为 codesnippets ### 思考 - 封装好 codesnippets 之后团队除了你编写这个项目的人如何使用/uff1f如何知道是否有这个代码块/uff1f 方案/uff1a先在团队内召开代码规范会议/uff0c大家都统一知道这个事情在。之后大家共同维护 codesnippets。用法见下 属性/uff1a通过 **Property_类型** 开头/uff0c回车键自动补全。比如 Strong 类型/uff0c编写代码通过 Property_Strong 回车键自动补全成如下格式 ```objective-c @property (nonatomic, strong) *; ``` 方法/uff1a以 **Method_关键词** 回车键确认/uff0c自动补全。比如 Method_UIScrollViewDelegate 回车键自动补全成 如下格式 ```objective-c #pragma mark - UIScrollViewDelegate - (void)scrollViewDidScroll:(UIScrollView *)scrollView { } ``` 各种常见的 Mark/uff1a以 **Mark_关键词** 回车确认/uff0c自动补全。比如 Method_MethodsGroup 回车键自动补全成 如下格式 ```objective-c #pragma mark - life cycle #pragma mark - public Method #pragma mark - private method #pragma mark - event response #pragma mark - UITableViewDelegate #pragma mark - UITableViewDataSource #pragma mark - getters and setters ``` - 封装好 codesnippets 之后团队内如何统一/uff1f想到一个方案/uff0c可以将团队内的 codesnippets 共享到 git/uff0c团队内的其他成员再从云端拉取同步。这样的话团队内的每个成员都可以使用最新的 codesnippets 来编码。 编写 shell 脚本。几个关键步骤/uff1a 1. 给系统文件夹授权 2. 在脚本所在文件夹新建存放代码块的文件夹 3. 将系统文件夹下面的代码块复制到步骤2创建的文件夹下面 4. 将当前的所有文件提交到 Git 仓库 ## 文件模版的改造 我们观察系统文件模版的特点/uff0c和在 Xcode 新建文件模版对应。 ![Xcode file template存放地址](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/20190304_filetemplates.png) 所以我们新建 Custom 文件夹/uff0c将系统 Source 文件夹下面的 Cocoa Touch Class.xctemplate 复制到 Custom 文件夹下。重命名为我们需要的名字/uff0c我这里以/u201cPower/u201d为例 ![自定义文件模版示例](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/20190319-filetemplateSelf.png) 进入 PowerViewController.xctemplate/PowerViewControllerObjective-C 修改 `___FILEBASENAME___.h` 和 `___FILEBASENAME___.m` 文件内容 ![注意点1](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/20190304-fileTmplates3.png) 在替换 .h 文件内容的时候后面改为 UIViewController/uff0c不然其他开发者新建文件模版的时候出现的不是 UIViewController 而是我们的 PowerViewController ![.m文件内容](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/20190304_filetemplates4.png) 修改 TemplateInfo.plist ![plist注意点](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/20190319-filetemplate5.png) 思考/uff1a - 如何使用 商量好一个标识/uff08/u201cPower/u201d/uff09。比如我新建了单例、控制器、Model、UIView、UITableViewCell、UICollectionViewCell6个模版/uff0c都以为 Power 开头。 ![模版用法](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/20190319-filetemplate6.png) - 如何共享 以 shell 脚本为工具。使用脚本将 git 云端的代码模版同步到本地 Xcode 文件夹对应的位置就可以使用了。关键步骤/uff1a 1. git clone 代码到脚本所在文件夹 2. 进入存放 codesnippets 的文件夹将内容复制到系统存放 codesnippets 的地方 3. 进入存放 file template 的文件夹将内容复制到系统存放 file template 的地方 ## 内容及其如何使用 1. Property 属性。敲 **Property_** 自动联想/uff0c光标移动选中后敲回车自动补全 2. Mark 标识。 敲 **Mark_** 自动联想/uff0c会展示各种常用的 Mark/uff0c光标移动选中后敲回车自动补全 3. Method 方法。敲 **Method_** 自动联想/uff0c会展示各种常用的 Method/uff0c光标移动选中后敲回车自动补全 4. GCD。敲 **GCD_** 自动联想/uff0c会展示各种常用的 GCD/uff0c光标移动选中后敲回车自动补全 5. 常用 UI 控件的懒加载。敲 **_init** 自动联想/uff0c展示常用的 UI 控件的懒加载/uff0c光标移动选中后敲回车自动补全 6. Delegate。敲 **Delegate_** 自动联想/uff0c会展示各种常用的 Delegate/uff0c光标移动选中后敲回车自动补全 7. Notification。敲 **NSNotification_** 自动联想/uff0c会展示各种常用的 NSNotification 的代码块/uff0c比如发送通知、添加观察者、移除观察者、观察者方法的实现等等/uff0c光标移动选中后敲回车自动补全 8. Protocol。敲 **Protocol_** 自动联想/uff0c会展示各种常用的 Protocol 的代码块/uff0c光标移动选中后敲回车自动补全 9. 内存修饰代码块 10. 工程常用 TODO、FIXME、Mark。敲 **Mark_** 自动联想/uff0c会展示各种常用的 Mark 的代码块/uff0c光标移动选中后敲回车自动补全 11. 内存修饰代码块。敲 **Memory_** 自动联想/uff0c会展示各种常用的内存修饰的代码块/uff0c光标移动选中后敲回车自动补全 12. 一些常用的代码块。敲 **Thread_** 等自动联想/uff0c选中后敲回车自动补全。 ## 使用 ```shell chmod +x ./syncSnippets.sh // 为脚本设置可执行权限 chmod +x ./uploadMySnippets.sh // 为脚本设置可执行权限 ./syncSnippets.sh // 同步git云端代码块和文件模版到本地 ./uploadMySnippets.sh //将本地的代码块和文件模版同步到云端 ``` ## PS **不断完善中。大家有好用或者不错的代码块或者文件模版希望参与到这个项目中来/uff0c为我们开发效率的提升添砖加瓦、贡献力量** 目前新建了大概58个代码段和6个类文件模版/uff08UIViewController控制器带有方法组、模型、线程安全的单例模版、带有布局方法的UIView模版、UITableViewCell、UICollectionViewCell模版/uff09 shell 脚本基本有每个函数和关键步骤的代码注释/uff0c想学习 shell 的人可以看看代码。[代码传送门](https://github.com/FantasticLBP/codesnippets)