iOS developer的良好习惯

前言

隐藏细节,暴露抽象。git

做为一名有追求的工程师,咱们但愿代码可以在版本迭代中逐渐优化而不是劣化;同时也会学习掌握更多的技巧和工具,去更好的设计、实现和组织代码。偶然看到一个apple工程师的分享,因而加上一些本身的经验和感觉,作一些总结。编程

正文

1、代码组织

一、使用group

做为一名iOS工程师,Xcode应该是最熟悉的工具之一。旧版本的Xcode在新建一个目录时,只会做为建立一个引用,不会同时在相同的路径下去建立目录。新版本Xcode建立目录的时候都是以group的形式去建立,会在同级路径下去建立对应的目录。
好比下图,在建立New Group的时候,就会一样在Audio的目录下去建立一个New Group的目录。若是项目的代码是好久之前的Xcode建立的,最好检查一遍目录,使得Xcode的工程文件目录和实际的文件目录结构保持一致;若是项目是新Xcode建立则尽可能在Xcode中建立group。 xcode

二、拆分大文件

若是项目有使用storyboard,则能够把较大的storyboard文件,经过引用的方式拆分红多个storyboard。这样能提高打开时的速度,也能使得多人协同开发时减小冲突的产生。
可是我经历过的项目都没有使用storyboard,大文件的矛盾更可能是产生在.m文件,以一个咱们项目中的文件为例:性能优化

这个2000行的.m文件并非一蹴而就,而是随着十几个版本的迭代,逻辑不断增长,慢慢变大的文件。这也是咱们常说的历史技术债务。技术债务产生的缘由多种多样,多是最开始的时候没有很好的框架设计,也多是实现过程当中有不规范的现象,又或者是多人协做开发致使的代码膨胀。当发现问题以后,就须要去偿还这个技术债务。markdown

.m文件拆分首先须要把业务的核心逻辑梳理出来,抽象出来该模块的状态信息、关键参数,将外部业务在.m内添加的逻辑改成依赖.m提供的状态,而状态能够经过通知、消息等方式抛出去;
核心可是又内聚的逻辑可使用xxLogic去封装,而后.m文件直接依赖该xxLogic;也能够将其聚合到.m文件的一个Category之中;
通过这番处理,.m文件能获得极大瘦身,而梳理完内外部依赖以后,后续再新增逻辑也不用去查看.m文件,而是依赖下面的.h文件。
网络

三、重视Xcode的提示

保持Xcode工程设置是最新的,使用Xcode自带的性能优化。当Xcode弹出下面这个框的提示时,若是没有特殊诉求,apple工程师推荐点击Perform Changes按钮。多线程

在编译的过程当中,Xcode给出的warning可能在线上运行时就是一个Bug。建议在debug开发阶段,打开Treat Warnings as Errors选项;追本溯源,找到问题的根本缘由,解决每个编译期间的warning。架构

若是是已知问题,暂无解决方案,为了不阻塞编译运行,可使用xcode指令去忽略。(具体的warning类型能够在Xcode的Issue Navigator查看,快捷键是command+5)app

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// ingore code
#pragma clang diagnostic pop
复制代码
四、去掉无用的代码

咱们有时会提交一部分被注释代码,理由多是代码如今不须要但下个版本可能会用到,临时注释一下,反正不影响运行。可是设想一下,若是团队里面每一个人都有这个习惯,那么项目中是否会存在不少无用的代码?而且这个代码可能永远也不会有用武之地。
因此,果断地删除那些无用代码吧,即便真的有须要用到的时候,也能够经过代码的版本控制工具去找到那些历史代码。框架

2、代码管理

版本控制系统已经成为开发的必备工具之一。曾经svn也是版本管理的高效工具,Windows系统中的小乌龟(TortoiseSVN)很是好用。可是随着git的出现,svn已经被逐渐淘汰。

一、提交独立

一个复杂功能每每由多个需求点组成,开发过程也可能持续数天时间。能够把需求的提交拆分红屡次,尽可能使得单次提交独立,Xcode能够看到每一行代码的提交备注信息。 换位思考,咱们但愿从git的commit信息里面,看到这段代码的原因。
点一下右边对应信息,选择show commit,还能够看到对应commit的具体内容。

一我的能够记住昨天为何写这段代码,但很难记住一段数月乃至数年前的代码为什么出现。

二、分支管理

为了保持开发阶段的便利,提供alpha分支,做为平常开发的合入分支;为了保证外网代码的可查,提供beta分支,做为版本发布的打包分支;当版本发布以后,还须要打tag记录对应版本,好比说release_1.0.0.10。
平常的需求开发(feature分支)、问题修复(bug分支)都是在非主干分支进行开发,最终再合入alpha分支。合入的要求根据团队实际状况,能够是分支验收完成再合入,也能够合入后统一验收。

三、Code Review

Code Review(代码审查,后面简称CR)是发生在分支合入的状况,是成熟开发团队必不可少的环节。CR有助于团队代码风格的统一,包括函数命名、变量命名、代码组织风格等。同时,CR要求代码具有必定的可读性,也要求单次提交不过包括过多改动。

3、文档

一、必要的注释

好的代码一目了然,能清晰描述逻辑,不须要注释来辅助描述。可是一段特殊逻辑,须要有注释来描述为什么存在,以方便在改动以后去回归影响点。
好比说一段经典的dispatch_after 1秒的逻辑,这1秒多是为了不某些异常case,也多是产品侧的需求要求。

二、对外方法的描述

平时的开发过程,除了注意变量和方法的命名要具备含义,对外提供方法的注释能够清晰描述须要的参数。好比说下面的一个方法:

在Xcode中选择对应的方法,按下快捷键option+?就能够看到该方法的描述,以及各个参数的要求。若是方法还没添加描述,则按下option+command+?自动生成待补充的描述。

三、文档积累

随着业务的发展,项目中代码不可避免的会快速膨胀,直接阅读代码会很是吃力。此时就须要有文档来辅助了解各个模块的状况。
文档应当避免对具体逻辑细节的赘述,更是和从总体的设计和考虑的因素出发,描述该模块是如何运行起来。同时在设计的过程,也应该基于以前的技术方案设计。

培养团队的写文档习惯,每一个版本前期组织技术方案评审,由负责较为复杂需求的工程师准备一份技术方案的设计文档,能够达到事半功倍的效果。

4、便捷工具

你们提到Xcode的分析工具,第一反应每每是Instrucment中的工具集。可是实际开发中还有一些便捷工具。

一、Network Link Conditioner

模拟弱网络环境,之前是在手机的设置-开发者-Network Link Conditioner能够去设置,如今真机链接以后能够在Xcode中按下command+shift+2,选择对应的设备就能够选择具体的网络环境。

二、 Address Sanitizer

Address Sanitizer是内存错误检测工具,经过malloc/free增长标记实现。 好比说下面这一段代码,buf指针建立了1024内存,再手动释放,而后再去访问buf指针的元素。这段代码编译时正常,在运行时不必定会崩溃 ,有可能就会演化成一个偶现bug,难以定位。 在使用Address Sanitizer工具的时候,运行到130行时就会报错:Use of dealloccated memory

打开方式是在scheme选项中,勾选Address Sanitizer。

三、Thread Sanitizer

Thread Sanitizer是线程错误检测工具,能够检测到一些多线程数据访问的错误,好比说下面的代码。 sTestNum是静态全局变量,建立了多个线程去操做该变量,会触发Data Race

打开方式是在scheme选项中,勾选Thread Sanitizer。

Thread Sanitizer关注的是数据的多线程访问,经过记录内存的访问来实现,并不能定位到多线程的crash问题,好比下面这个crash:

四、Main Thread Checker

Main Thread Checker是多线程操做UI检查工具,UI操做只能在主线程执行,若是在子线操做则会触发警告。

打开方式是在scheme选项中,勾选Main Thread Checker。

五、Debug Gauges

在debug运行程序的时候,Debug Gauge能快捷地查看CPU、Memory、Disk、Network信息。

打开方式是Xcode按下command+7。

5、开发建议

一、最小依赖原则

一段逻辑的运行,每每须要外部的变量输入。有时候为了便捷开发,函数调用时候不会传递参数,而是经过全局变量、self指针等直接去获取须要的数据。可是这样会致使代码逻辑紊乱。在编码的时候,很是建议使用最小依赖原则:尽量少的使用外部依赖。
以函数为例,一个xx逻辑处理的方法应该只依赖函数参数。这样函数的输入输出是固定的,即便函数放到其余地方,只要保证函数的输入不变,则逻辑的输出是不变的。
同理,除了函数还有view、model等等,尽量少的去依赖外部数据、外部模块,则该处逻辑更加独立,更容易实现能够直接复用的view、model等等。

二、组件化&模块化

实现功能的时候,应尽量去除耦合;特定功能组成的库就是组件,写新功能代码尽量要往组件方向实现;而模块化指的是根据业务形态,把代码按照功能、业务进行聚合,至关于组合了各类组件和业务逻辑的库。
模块化和组件化等一个重要特色就是Pod化,将这些特定、独立的功能代码和业务代码从主工程中剥离,抽象出来业务须要的接口,再从新经过pod依赖引入主工程。在这个过程,不仅仅是把代码转移到Pod库,还须要作一些业务的解耦和依赖抽象。
好处也是显而易见:
开发上,模块化后各个业务相对独立,可以更加专一本身业务逻辑,即便业务出错影响面也比较可控;
效率上,模块化后能够作二进制组件,加快编译速度;
管理上,组件owner的意识更强,方便添加数据监控;
架构上,强迫面向接口编程,避免大量耦合的胶水代码。

总结

本文部分参考自 WWDC2019,结合一些工做经验,作了更适合本身的阐述。 本身也梳理了接下来一段时间的技术优化方向: 平常业务迭代,经过CR保证新增代码风格统一; 复杂业务需求,须要作技术方案评审,集思广益; 已有历史债务,小模块微整实现,大业务走专项重构,注意人力投入、业务影响和收益评估。

相关文章
相关标签/搜索