1、CodeReview
Code Review 中文应该译作 “代码审查” 或是 “代码评审”,这是一个流程,当开发人员写好代码后,需要让别人来 review 一下他的代码,这是一种有效查找系统缺陷的方法。由此,我们可以审查代码的风格、逻辑、思路 。。。。。。,找出问题,以及改进代码,保证软件总体质量和提升开发者自身水平。因为这是代码刚刚出炉的时候,所以,这也是代码重构,代码调整,代码修改的最佳时候。所以,Code Review 是编码实现中最最重要的一个环节。
代码评审通常有两种类型:正式代码评审(formal code review),轻量级代码评审(light code review)。
1.1 为什么要进行代码评审
a、可以通过大家的建议增进代码的质量。
b、是一个传递知识的手段,可以让其它并不熟悉代码的人知道作者的意图和想法,从而可以在以后轻松维护代码。
c、鼓励程序员们相互学习对方的长处和优点,提高团队整体水平。
d、可以被用来确认自己的设计和实现是一个清楚和简单的。对开发人员来说也是一次开发思想重新重构的过程,可以更好的理解系统。
1.2 代码评审的方式和时间
a、由作者启动评审,展示评审文档(over-the shoulder)。它的优点是开发团队内部可以快速进行评审交流,对于项目架构,后期可扩展性,可维护性等可以启用这样的评审,通过启动这样的评审,一方面评审者可以学习作者的架构思想,另一方面也可以团队内部讨论架构上的不足加以改进。
b、组织评审会议(review meeting),由团队成员轮流选出自己的评审作品共其它评审者学习。可以在项目结束后进行,通过这种敦促的方式共享好的东西让整体团队一起进步。
c、利用评审工具(tool assited code review)。
1.3 代码评审的层级
a、实现正确性:主要包括是否实现预期功能,是否存在 bug,是否存在性能问题等。
b、设计合理性:主要包括实现方法,数据接口,设计模式,扩展性等,是否存在大量重复代码和其它组件是否有重复代码,包括结构设计是否合理,是否存在性能问题等。
c、代码可读性:主要包括代码风格,美观性。
实现正确性和设计合理性是必须要进行的 code review。对于代码可读性,由于每个人的编码风格不一样,建议做最低级别的 review,比如注释。
1.4 使用代码评审的注意点
a、Code reviews 不应该承担发现代码错误的职责。Code Review 主要是审核代码的质量,如可读性,可维护性,以及程序的逻辑和对需求和设计的实现。代码中的 bug 和错误应该由单元测试,功能测试,性能测试,回归测试来保证的(其中主要是单元测试,因为那是最接近 Bug,也是 Bug 没有扩散的地方)。
b、Code reviews 不应该成为保证代码风格和编码标准的手段。编码风格和代码规范都属于死的东西,每个程序员在把自己的代码提交团队 Review 的时候,代码就应该是符合规范的,这是默认值,属于每个人自己的事情,不应该交由团队来完成,否则只会浪费大家本来就不够的时间。个人认为 “meeting” 是奢侈的,因为那需要大家在同一时刻都挤出时间,所以应该用在最需要的地方。代码规范比起程序的逻辑和对需求设计的实现来说,太不值得让大家都来了。
10 年前,上面这两件事会是理所当然的(10 年前的中国的软件开发还没有 Code Reivew 呢),今天,在中国的很多公司上面这两件事依然被认为是 Code Reivew 最重要的事,所以,能够看到很多开发 Team 抱怨 Code Review 就是一个形式,费时费力不说,发现的问题还不如测试,而评审者们起初在代码风格上有些见术,别的也就没什么用了,长而久之,大家都会开始厌烦这个事了。
所以,在今天,请不要把上面的那两件事分散了 Code Review 的注意力,取而代之的是,对于 Bug,程序的作者要在 Review 前提交自己的单元测试报告(如:XUnit 的测试结果),对于代码规范,这是程序作者自己需要保证的,而且,有一些工具是可以帮你来检查代码规范的。
当然,上述这些言论并不是说,你不能在 Code Review 中报告一个程序的 bug 或是一个代码规范的问题。只是说,那并不是 Code Review 的意图。
1.5 使用代码评审的小提示
1、经常进行 Code Review
以前经历过几个相当痛苦的Code Review,那几次Code Review都是在程序完成的时候进行的,当你面对那近万行的代码,以前 N 我掺和在一起的功能,你会发现,整个 Code Review 变得非常地艰难,用不了一会儿,你就会发现大家都在拼命地打着哈欠,但还是要坚持,有时候,这样的 Review 会持续 3 个小时以上,相当的夸张。而且,会议上会出现相当多的问题和争论,因为,这就好像,人家都把整个房子盖好了,大家 Review 时这挑一点那挑一点,有时候触动地基或是承重墙体,需要大动手术,让人返工,这当然会让盖房的人一下就跳起来极力地维护自己的代码,最后还伤了团队成员的感情。
所以,千万不要等大厦都盖好了再去 Reivew,而且当有了地基,有了框架,有了房顶,有了门窗,有了装修的各个时候循序渐进地进行 Review,这样反而会更有效率,也更有帮助。
下面是一些观点,千万要铭记:
要 Review 的代码越多,那么要重构,重写的代码就会越多。而越不被程序作者接受的建议也会越多,唾沫口水战也会越多。
程序员代码写得时候越长,程序员就会在代码中加入越来越多的个人的东西。 程序员最大的问题就是 “自负”,无论什么时候,什么情况下,有太多的机会会让这种 “自负” 澎涨开来,并开始影响团队影响整个项目,以至于听不见别人的建议,从而让 Code Review 变成了口水战。
越接近软件发布的最终期限,代码也就不能改得太多。
个人的习惯,也是对团队成员的要求是:先 Review 设计实现思路,然后 Review 设计模式,接着 Review 成形的骨干代码,最后 Review 完成的代码,如果程序复杂的话,需要拆成几个单元或模块分别 Review。当然,最佳的实践是,每次 Review 的代码应该在 1000 行以内,时间不能超过一部电影的时间
1.5 小时。当然,在敏捷开发中,他们不需要 Code Reivew,其实,敏捷开发中使用更为极端的 “结对编程”(Pair-Programming)的方法:一种时时刻刻都在进行 Code Review 的方法,个人感觉在实际过程中,这种方法有点过了。另外,大家可以看看《结对编程的利与弊》来了解一下这种方法的问题。
2、Code Review 不要太正式,而且要短
忘了那个代码评审的 Checklist 吧,走到你的同事座位跟前,像请师父一样请他坐到你的电脑面前,然后,花 5 分钟给他讲讲你的代码,给他另外一个 5 分钟让他给你的代码提提意见,这比什么都好。而如果你用了一个 Checklist,让这个事情表现得很正式的话,下面两件事中必有一件事会发生:
只有在 Checklist 上存在的东西会被 Review。
Code Reviews 变成了一种礼节性的东西,你的同事会装做很关心你的代码,但其实他心里想着尽快地离开你。
只有不正式的 Code Review 才会让你和评审者放轻松,人只有放松了,才会表现得很真实,很真诚。记住 Review 只不过是一种形式,而只有通过相互的讨论得到了有意义和有建设性的建议和意见,那才是最实在的。不然,作者和评审者的关系就会变成小偷和警察的关系。
3、尽可能的让不同的人 Reivew 你的代码
这是一个好主意,如果可能的话,不要总是只找一个人来 Review 你的代码,不同的人有不同的思考方式,有不同的见解,所以,不同的人可以全面的从各个方面评论你的代码,有的从实现的角度,有的从需求的角度,有的从用户使用的角度,有的从算法的角度,有的从性能效率的角度,有的从易读的角度,有的从扩展性的角度……,啊,好多啊,还让不让人活了。不管怎么说,多找一些不同的人会对你很有好处。当然,不要太多了,人多嘴杂反而适得其反,基本上来说,不要超过 3 个人,这是因为,这是一个可以围在一起讨论的最大人员尺寸。
下面是几个优点:
- 从不同的方向评审代码总是好的。
- 会有更多的人帮你在日后维护你的代码。
- 这也是一个增加团队凝聚力的方法。
4、保持积极的正面的态度
再说一次,程序最大的问题就是 “自负”,尤其当我们 Review 别人的代码的时候,我已经见过无数的场面,程序员在 Code Review 的时候,开始抨击别人的代码,质疑别人的能力。太可笑了,我分析了一下,这类的程序员其实并没有什么本事,因为他们指责对方的目的是想告诉大家自己有多么的牛,其实,靠这种手段来表现自己的程序员,其实是就是传说中所说的 “半瓶水”。
所以,无论是代码作者,还是评审者,都需要一种积极向上的正面的态度,作者需要能够虚心接受别人的建议,因为别人的建议是为了让你做得更好;评审者也需要以一种积极的正面的态度向作者提意见,因为那是和你在一个战壕里的战友。记住,你不是一段代码,你是一个人。
5、学会享受 Code Reivew
- 这可能是最重要的一个提示了,如果你到了一个人人都喜欢 Code Reivew 的团阿,那么,你会进入到一个生机勃勃的地方,在那里,每个人都能写出质量非常好的代码,在那里,你不需要经理的管理,团队会自适应一切变化,他们相互学习,相互帮助,不仅仅是写出好的代码,而且团队和其中的每个人都会自动进化,最关键的是,这个是一个团队。
2、实用的 Code Review 工具
- 长时间以来,Code Review 需要有一些有效的工具来支持,这样我们就可以更容易,更有效率地来进行代码审查工作。下面是 5 个开源的代码审查工具,他们可以帮助你更容易地进行这项活动。
2.1 Review board
Review board 是一个基于 web 的工具,主要设计给 django 和 python 的用户。Review board 可以帮助我们追踪待决代码的改动,并可以让 Code Review 更为容易和简练。尽管 Review board 最初被设计在 VMware 项目中使用,但现在其足够地通用。当前,其支持这些代码版本管理软件:SVN、CVS、Perforce、Git、Bazaar 和 Mercurial。
Yahoo 是 Review board 的其中一个用户。
- “Review board 已经改变了代码评审的方式,其可以强迫高质量的代码标准和风格,并可以成为程序员编程的指导者。每一次,当你访问 search.yahoo.com 时,其代码都是使用 Review board 工具 Review 过的。We’re great fans of your work!” – Yahoo! Web Search。
2.2 Codestriker
Codestriker 也是一个基于 Web 的应用,其主要使用 GCI-Perl 脚本支持在线的代码审查。Codestriker 可以集成于 CVS, Subversion, ClearCase, Perforce 和 Visual SourceSafe。并有一些插件可以提供支持其它的源码管理工具。
David Sitsky 是 Codestriker 的作者,并也是最活跃的开发人员之一。Jason Remillard 是另一个活跃的开发者,并给这个项目提供了最深远最有意义的贡献。大量的程序员贡献他们的代码给 Codestriker 项目,导致了这个项目空前的繁荣。
2.3 Groogle
Groogle 是一个基于 Web 的代码评审工具。Groogle 支持和 Subversion 集成。它主要提供如下的功能:
- 各式各样语言的语法高亮。
- 支持整个版本树的比较。
- 支持当个文件不同版本的 diff 功能,并有一个图形的版本树。
- 邮件通知所有的 Reivew 的人当前的状态。
- 认证机制。
2.4 Rietveld
Rietveld 由 Guido van Rossum 开发(他是 Python 的创造者,现在是 Google 的员工),这个工具是基于 Mondrian 工具,作者一开始是为了 Google 开发的,并且,它在很多方面和 Review board 很像。它也是一个基于 Web 的应用,并可以 Google App Engine 当主机。它使用了目前最流行的 Web 开发框架 django 并支持 Subversion 。当前,任何一个使用 Google Code 的项目都可以使用 Rietveld 并且使用 python Subversion 服务器。当然,它同样支持其它的 Subversion 服务器。
2.5 JCR
JCR 或者叫做 JCodeReview 也是一个基于 Web 界面的最初设计给 Reivew Java 语言的一个工具。当然,现在,它可以被用于其它的非 Java 的代码。
JCR 主要想协助:
审查者。所有的代码更改都会被高亮,以及大多数语言的语法高亮。Code extracts 可以显示代码评审意见。如果你正在 Review Java 的代码,你可以点击代码中的类名来查看相关的类的声明。
项目所有者。可以轻松创建并配置需要 Review 的项目,并不需要集成任何的软件配置管理系统(SCM)。
流程信仰者。所有的评语都会被记录在数据库中,并且会有状态报告,以及各种各样的统计。
架构师和开发者。这个系统也可以让我们查看属于单个文件的评语,这样有利于我们重构代码。
JCR 主要面对的是大型的项目,或是非常正式的代码评审,从这方面看来,他并不像上面的那些工具。
2.6 Jupiter
- Jupiter,是另一个代码 review 的工具你可以去考虑使用的,它是一个 Eclipse IDE 的插件。
3、改善代码质量的一些方法
3.1 减少对象属性
这个是最容易改善代码质量的一个点,很多代码一眼看上去就会让人感觉很凌乱,一上来就是几十个不同的对象变量定义在里面,这让不同逻辑之间莫名其妙没法分开。一个是定义的方式不对,很多莫名其妙的内部变量暴露在头文件中,让外部调用者根本不知道哪些才是 public 可以操作的方法。另外实际上,大多数是可以通过局部变量或者
__block
变量来代替的。1、头文件中尽可能少暴露变量或方法,而要使用 extension 或者 category 放在 .m 文件,或者专门的 private 头文件中,头文件中暴露的信息越少越好,一切不必要的信息都不要暴露出来。.m 文件的 extension 中,定义 conforms protocol 和对象属性,对于对象属性的定义,使用 getter/setter 来定义。
2、使用局部变量或者
__block
变量代替局部变量不需要多说,需要写码的时候思路清晰一些,写完之后在 commit 之前即使 review 一定要 check 一遍,对自己的代码质量负责,code review 往往检查不出来冗余或者废弃的代码。不添加一个多余的对象属性,不留注释掉的代码,不留没有用途的代码,这些都是基本功,但是很多开发者就是做不到,或者说对写码没有爱,所以很多废弃的代码,我重构代码的时候,虽然对业务不熟悉,但是大多数模块都能删除掉十分之一的代码和大量的对象属性,这个是单纯的不够用心。
比如这里,使用 block 的时候回传一些变量。
再比如这里,需要记录一个 pan 手势开始时,headerView的顶部坐标,结合 RAC 之后,本来需要全局变量来记录的值,使用
__block
变量即可搞定。
3、可以尽可能避免循环引用
- 有个地方很多开发者会疏漏,在 block 中使用
_XXX
对象变量的时候,block 会 retain self 指针,一不小心就会造成循环引用的出现。所以使用局部变量的话,就能扼杀这种问题在摇篮之中。
- 有个地方很多开发者会疏漏,在 block 中使用
3.2 减少和模块化对象消息
1、减少对象消息
- 减少 UI 的 action 类消息,感谢 block 和 RAC,或者 blockskit,让我们得以通过 hook 来把之前 target-action 模型换为 block 来实现,UI 和 action 的代码终于可以一起了,使整个逻辑变得紧凑,在查看代码的时候终于不用跳来跳去了。还有就是日常开发中,把自己写的各种 protocol 或者传递 target/selector 的地方,尽量使用 block 来代替,相信我,这个会使代码好读很多。
2、模块化
使用
“#pragma mark - XXX”
进行分割不同逻辑之间的界限,让整个文件阅读起来更加结构化。还有一个我现在最常用的就是是设置 Xcode 的快捷键,把 Ctrl + 6 显示文档结构的快捷键改为:Command + D ,搜索来快速跳转到对应的消息和模块,要尽量避免文档结构显示超过两屏幕,超过两屏幕说明有点多了,你肯定考虑一下重构了。个人习惯一般划分的模块有:life cycle,ui helper,datasource/delegate,依据功能进行划分的模块等等。
3.3 MVVM && RAC
MVVM 不一定需要使用 RAC,但是 data binding 少不了,在 iOS 中也就是 KVO 了,建议大家都去尝试一下,这个基本上是 MVVM的 最核心的东西了,连 Android SDK 也不得不引入这个特性。把数据部分的逻辑抽取放在 ViewModel 中,然后让 UI 和 ViewModel 中的数据 binding,这个不会减少代码量,但是绝对可以大大简化开发时逻辑的重复度,再也不用重写
-setXXX:
方法来 update 一大堆不相关的 UI 了。关于 RAC,即使你不使用 RAC,有一些东西也是绝对值得你在项目中引入的,比如
@weakify(self)/@strongify(self)
,通过预编译查看的话,这个的做法是设置一个局部变量 self 来覆盖全局的 self,进而避免循环引用的,需要注意的是 block 层次较深的时候使用的问题。
3.4 UI 开发
1、重写 setter 方法和 Code Block Evaluation C Extension 语法
2、复杂 UI 的开发
有时候我们开发业务的时候,产品需求往往非常复杂,酷炫的 UI 加上各种考虑全面的逻辑,这个的结果就是,码农的超长代码,而我们平时工作面对的也大多数都是这类问题。关于这个问题,解决方式是组合式 UI / custom view / child view controller 来解决。
1)组合式 view
这个概念是从 Android 中借鉴而来。重构时查看项目中的代码,发现大家用的做 UI 的时候,对这个概念不是很强烈,感觉是对 UIView 的 view hierarchy 理解不够。比如一个复杂的 UI,直接把所有的 subviews 直接堆积到 super view 上面,这样的结果就是,调整 subview 的 frame 非常困难。我个人的做法是,首先对复杂 UI 进行分块,从左到右或者从上倒下,把各个 UI 元素放到不同的 container view 上面,然后组合这些 container view 放到 super view 上面,这样的好处非常明显,首先 UI 干净清晰,阅读起来不那么费劲。其次就是你计算坐标或者设置约束会变得很简单,因为你调整一个 UI 元素的时候,只需要考虑它与包含它的 container view 的坐标关系即可,而不是通过一大堆无趣计算跟最外层 super view 关联起来。还有就是可以充分利用 Auto Layout 和 autoresiziingmask 这些 UI 利器,使用的时候会非常方便。再有就是结合 RACObserver 这个利器之后,你能很容易做到根据 data 来 update ui。
举个例子,是我们项目中前一段时间我重构的一个页面,这个首页列表,性能要求比较高。并没有使用 Auto Layout 来实现,但是不使用 Auto Layout 并不是不把它写的很干净的理由。
这是我对一个 UITableViewCell 的分层,最外层由 icon view / right view / bottom view 这些 container view 组成,而 right view 这个 container view 则又是由 right top view / right middle view /right bottom view 这些 sub container view 组合而成,而具体的 UI 元素则是放在这些 sub container view 之中。这样 UI 代码就会以一种层次化样式展示出来,init/layoutsubviews 只需要维护 self 与container view 的关系即可,而具体展示数据的 UI 元素也只跟 sub container view 存在坐标关系。我们看一下 right view 这个 container view 的代码实现:
关于性能的话,感谢 iOS,我们不存在 Android 中页面层次较深性能卡顿的问题,放心把 UI 层次化就行。
2) custom view
对于非常复杂并且相对独立或者可以重用的 UI,及时使用 custom view 子类化。对于单纯的展示 UI,我们只需要简单通过组合式 view 就可以实现了。但是有时候,我们会遇到一些包含无论是动画,逻辑都比较复杂的情况,这个时候使用组合式 View 去实现,一方面容易把逻辑弄混乱,会把文件的文档结构变得很复杂,简单来说就是对象的消息数量很多。这个时候,我们可以通过 custom view 来实现,实际上这个也是组合式 view,但是我们是把这些组合式 view 变成了一个类而已,只暴露少量的接口给外部调用。如果这个 custom view 会出现在多个业务模块中,那么有必要使用一个单独的文件来容纳这个类,如果仅仅是这个模块一个使用的话,可以直接写在这个业务模块的文件中即可,没有必要对所有的类都单独一个文件,我们就当作这个 “内部类” 来弄了。
什么时候使用 custom view 而不是组合 view,我想了很久,你觉得组合式 view 的代码很乱的时候,别客气,包装为一个 custom view 就行了。我这边最近遇到的几个问题是使用 UICollectionView 来做部分 UI 的时候,同时还有其他很多 UI 元素,我会写一个 custom view。比如把一个左右滑动查看图片的 UI 使用 PhotoView 这个 custom view 进行包装,内部使用 UICollectionView 实现一部分相对独立的模块,这个时候这个控件实际上是可以包装为一个相对独立的模块的,用子类我感觉比较合适一些。
3) container view controller
这个用法很多开发者不熟悉或者说是用的不多,但实际业务中,这个技术非常有用途,可以大大提高开发效率。对于有相对独立业务逻辑以及生命周期要求的业务,使用 child view controller 进行包装,如果 parent view contrller 与 child view controller 之间非常密切,则使用 View Model 以及 block 来对 parent view controller 和 child view controller 进行衔接。
使用 child view controller 来开发 UI 而不是 custom view 的优势很多,我个人认为最大优势在于可以方便利用 View Controller 的生命周期以及 View Controller Hierarchy,比如在
-viewWillAppear/-viewDidDisappear
中做一些操作,再比如直接获取 UINavigationController 指针等等。之前的做法一般是在 View Controller 的对应生命周期内调用 custom view 的方法,传递 self.navigationController 指针给 custom view 等。所以可以不仅仅把 UI 相关的代码包装进入这个 child view controller,也可以把网络请求,数据处理这些这些逻辑放到 child view controller 中,这样下来就能避免那种动不动超过 1k 行的 view controller 的出现了。利用 MVVM 之后,还有一个比较有好处的用法,比如公用一些数据的时候,之前我们是把对象传递来传递去,这样的问题是很容易出现混乱,这个时候我们是传递 ViewModel 就可以避免这个问题,ViewModel 既负责网络请求又负责数据处理,而 parent view controller 与 child view controller 所需要做的事情就是跟 ViewModel 进行 binding 而已。
3.5 Auto Layout/Masonry
在一些性能要求不是那么强烈的非列表页,我们可以大量使用 Auto Layout 来开发 UI,充分利用 UI 根据数据的自适应能力,连在 container view 中调整 UI 的步骤都不需要了。
至于 Masonry 这个框架,之前我对这个抱有一定的怀疑不敢使用,所以我把源码读了一遍,发现这个包装很薄很巧妙,很多设计思路也值得借鉴,对源码有兴趣的可以参考博客。我读完源码之后,尝试着完全使用 Mansory 来开发一个展示信息的页面,感觉太爽了。
说一下 Auto Layout 的问题:
1、首先一个问题,是如果一个 view 不是 leaf view 的话,那么这个 UIView 如果 hidden 的话,它的约束仍然是 work 的,所以会留下空白,不会像 Android 中那样设置 GONE 那么方便。国内 sunny 大神开源一个不错的解决方式。这里说一下我之前的解决方式,比较土逼,直接子类化:
2、动画的问题
- 使用 Auto Layout 有一个比较大的问题在于动画,通过更改约束来进行动画,一直是我比较头疼的问题,所以一般遇到这类问题的时候,我都会尽量避免使用 Auto Layout 来解决,而是使用 frame 的方式来做。可以参考 objc.io 上面的一篇文章。
3、多行 UILabel 的问题
4、UIScrollView 的问题以及约束歧义和其他问题
参考文章。
这个地方,我的建议是根据具体问题来选择实现方式:spring & structs 也好,Auto Layout 也好,那种解决问题较为简洁快速就用那种,不一定非要固定于一种行为,尤其是开发的页面有大量动画的时候。
3.6 注释
不要写一堆中文注释,代码不要出现大量的中文,OC 已经够啰嗦,不要这么啰嗦地写码。除了提供服务的 public 功能或者方法,业务代码仅在某些关键点上注释一下就行,不需要一大堆中文,这样太 low,代码自注释即可。
而对于出现拼音命名代码的人,能做主的话,别犹豫,开掉吧。这里吐一下槽,之前的公司就有这样的哥们,不是我招进来的,老板硬塞给我的。
3.7 善用 OC 的新语法
OC 有很多新的语法糖,可以大大提高我们的效率,参考 Apple Guide。
比如打印数字的时候,我们可以用
@(xxx)
来打印,定义枚举的时候使用typedef NS_ENUM
,使用 instancetype 而不用 id 等等。
3.8 JSON 数据的处理
- 新手往往会被这个稍微困惑一下,比如服务器返回的数据格式不正确啦,包含 null 啦,都很容易引起项目崩溃。这个问题可以使用 Mantle 来解决,很多兄弟都在使用这个,我自己倒是一直没有用过。
3.9 block
使用 block 代替 delegate,这个没啥可多说的,把代码变得非常紧凑,减少文件的消息数量,最主要的是关系没那么紧密了。对于有大量的 delegate 方法才考虑使用 protocol 实现,这个时候 block 太多也影响阅读。
同时,对于传递 target/selector,也尽量使用 block 吧,这种阅读查找起来太不方便了。
3.10 提交代码
及时 stage,这个非常重要,开发过程中经常需要经常比对上一步的代码,这样才能最大程度上确保自己的改动是正确的。如果有一些小问题,也可以及时找到历史版本。
及时 commit,每完成一个相对完整的需求,就 commit,小提交是个好习惯。
PR code review 要做好,要花大量的时间做,有条件的话,最好每个版本开一次总结会。
3.11 RAC 封装网络请求
返回的 signal 要避免多次出现 side effect,但不使用 replay/replayLazily,因为 dispose 不会被调用。
使用 RACCommand 封装请求,查看这几篇文章:
- http://codeblog.shape.dk/blog/2013/12/05/reactivecocoa-essentials-understanding-and-using-raccommand/
- https://github.com/ReactiveCocoa/ReactiveCocoa/issues/963
- https://github.com/ReactiveCocoa/ReactiveCocoa/issues/1326。
结合 RACCommand 和 takeUntil: 来封装一个可以 cancel 的请求。