iOS大型项目开发漫谈

作者: CrespoXiao | 来源:发表于2015-08-24 00:08 被阅读16609次

    标题有些吓人请不要害怕,不过这确实不是扫盲贴,需要一定的iOS开发基础。在我多年的码农生涯中绝大部分时间都是做的小项目,大一些的可能也就是百万行代码的样子,跟Windows系统几千万行源码比简直就是小巫见大巫。不过,一个iOS项目的源码有数百万行算蛮大了。我想说的是,人总是会成长,会担当更大的责任接受更大的挑战,终有一天组织会有重要任务交给你。不过软件开发不是一朝一夕,也不会有多么的轰轰烈烈,更多的是平淡中无数的细节处理。做大型项目未必就需要多么高深的技术,也许就是细节的科学处理与规范的管理。

    首先说说编程语言的选择,Objecive-C还是Swift?我还没有在项目中使用Swift,因为我说服不了自己去用它,它的优势在哪里,你也不能强迫队友去学习Swift。当然,不用不代表不会,一入行就用Swift开发无意义产品的人没资格戴着有色眼镜鄙视不会Swift的同行。你知道Objecive-C与Swift混编有多少坑吗?你知道Swift也是跟Objecive-C共用一个Runtime环境吗?你知道使用了Swift会使得ipa包大10多M吗?Swift代表未来,Objecive-C是当下。Swift能做的Objective-C都能做,比如Closure完全就可用Blocks来代替,Tuple完全可以用结构体来代替。生产环境用Objective-C,业余观望Swift,这就是我的态度。Raywenderlich已经出了一堆Swift的教程,在前沿科技的使用上国内总是慢一个节拍,很大程度上那堵墙阻碍了国内IT从业者的发展,墙的开发者(包括我的信息安全工程老师)终有一天会受到历史的审判。

    再来说说应用架构,真是成也MVC,败也MVC。如果放任团队中的小朋友按他们所理解的MVC来开发,一般情况下出现的恶果就是类之间高耦合,Controller胖得不像话简直就成了百宝箱......每一次的版本迭代都会痛苦不堪,若功能不多还能勉强维持功能多了势必崩塌,就比如世博园中国馆吧,再按比例多盖5层试试看,呵呵。到头来开发人员实在受不了就只能选择重构要么跑路,后者更现实大家都懂,尤其是业务为王的企业里,代码质量算个屁只要能work,可是好的程序员都是有尊严的,deadline或是拍脑袋的政治任务通常会让程序员疲于应付,厌倦了也就该撤了。言归正传,既然MVC显得臃肿,那就是瘦身呗。通常瘦身后的MVC在iOS界被叫成MVCS或MVVM,这2个当然不是同一个东西,项目选用MVCS还是MVVM还是得看你的业务特性。MVCS与MVVM就那么完美吗?当然不,MVCS要注意Service/Store的滥用,其中的数据是否会被不同的模块污染。MVVM用得不好也会增加项目的维护难度,我说的是View和ViewModel之间松散的绑定关系,在iOS开发中一提到MVVM大家就想到ReactiveCocoa,其实没有ReactiveCocoa也可以啊,只是ReactiveCocoa的好处多多,如代码收敛不必写得到处都是,其他好处自己去挖掘吧。

    关于工程文件组织结构,很多人是Controllers/Views/Models/Vender...这样归类,其实这有个问题,比如你要找ControllerA的TableView用到的cellB类,你还要去Views里面一个个找,太痛苦了,就算search也还是苦毕竟不能所见即所得。我一般是按 Module来划分,每个Module里面有Controllers/Views/Models/Stores/API这样的分类,API是每一个接口的请求的封装,供Store调用,得到的数据解析实例化为Model再通过block传递给Controller去刷新View,很绕吗?使用RAC,Controller订阅Store里创建的RACSignal也能做到,U can make a try。还有就是Helper与Utility,与业务无关,具有对象性质,提供支持功能的代码放到Helper,比如创建一个自定义对象的封装。如果只是属于函数或算法,不是对象而且很多地方能用到,就放到Utility,比如排序/加密算法。

    工程文件组织结构

    关于网络通信模块的设计,我个人认为应该是每一个HTTP请求都应该独立互不干扰,就是你封装的POST/GET方法都会创建一个临时的对象,而长连接一般只维持一个实例对象采用单例的方式创建。我会为每一个HTTP 接口请求创建一个API对象,在里面请求数据,当然了为了避免请求代码的重复编写可以建立一个BASE API类,子类调用父类的请求方法就行了,不同的只是接口与参数。思想就是这样,以后有时间再来讲讲API类的设计。

    关于多线程,在iOS开发中用得最多的就是GCD和NSOperation了,在大部分情况下GCD就够用了。但是NSOperation也有它的优势,比如可以设置并发个数,可以设置线程间的依赖,可以暂停和恢复,比较灵活,多线程下载就经常用它。面试中也经常会问GCD与NSOperation的区别,这个自己去查查,资料还是比较丰富的,底下也有篇关于NSOperation的参考链接。GCD虽然强大,可是还是有很多人瞎用,真替他们着急,随便说几点:

    1.滥用dispatch_after做定时器导致crash!不知道还有个东西叫dispatch_source_set_timer吗?

    倒计时

    2.不要轻易用dispatch_sync,线程同步方法那么多为何你偏偏要爱上不该爱的它!尤其不要在dispatch_async里面用dispatch_sync,不要问为什么,百度里面google一下!

    3.dispatch_once也就创建单例的时候用用,不要瞎用。dispatch_once是整个app生命周期内只执行一次,不是对象生命周期内只执行一次,大哥!

    4.什么!你居然不知道用GCD创建串行线程与并行线程!

    串行 并行

    关于多团队协作,如果项目用分模块的方式进行开发,CocoaPods无疑是个非常好的工具。相对独立的模块尽量打成私有pod,这样,公共平台把整个项目的框架打好,其他的业务部门只需要自己建立一个私有pod,使用公共平台打好的那些私有pod独立开发调试就好,而不必时时提交代码到主工程,这样的话会非常麻烦,比如代码merge冲突,证书冲突,会疯掉的。当模块开发完毕再提交到主工程就好了。当然私有pod打多了也麻烦,私有pod依赖更多的私有pod在添加文件到target的时候会很痛苦,Xcode默认是target全部选上的,要跟Apple反馈一下希望他们会解决。(*Development Pods*里面添加文件需要选择target,默认都是选中的,而我们只需要添加到1个target里面去,依赖太多别的库就会有很多target。不过没关系,不用取消选择。添加完文件后执行一下pod install/pod update就行了,不用纠结那些选中的target。)

    关于数据持久化,最重要的就是保持数据源的一致。还有一个比较重要的就是APP升级之后的向前兼容,那种你一升级app启动崩溃的问题,多半是新版本的数据结构发生变化,不支持老版本产生的数据或者设置等。我一直偏爱sqlite,用得最多的就是FMDB,对CoreData无爱。要知道苹果自己的app NEWS用的就是FMDB啊,我还记得有人问facebook的工程师“为啥你们的app速度慢呢”,“because we use core data!”。有个开源库MagicRecord,对CoreData的深度封装,我用过,其实也还不错。

    另外,单元测试真的有必要吗?单元测试可以使逻辑清晰化,当你仅仅阅读单元测试代码的话,你会发现它们写的好像编程教科书里的伪代码。当TDD的时候,这一点尤其有用。通过写单元测试,你可以很快的把逻辑梳理清楚,然后用代码来实现它。单元测试可以降低代码的耦合度。我们知道,耦合度高的代码很难做单元测试,反过来,如果你必须做单元测试,你是不会把代码写的耦合度很高的。单元测试可以让你知道你对代码的修改是否影响到了原来就有的功能,但是这也是所有的回归测试都可以做的。单元测试的特点在于:它测试的东西足够小从而在代码重构后仍能复用。单元测试是唯一一个可能使覆盖率达到100%的测试。单元测试开始难,持续做的话会越来越容易,因为难主要是因为环境的搭建和桩函数的缺失。单元测试很容易定位Bug,它好像在你的程序中打了无数的断点。单元测试很费时间,不过,后续改Bug更费时间。

    讲了这么多,最后说说需求吧。我认为在需求评审时应该尽量抛出细节问题给pm,他们不懂技术,如果需求不确定就盲目开发,然后中途再改需求,这是很伤士气的,尤其是涉及到架构的修改,这让我想到那张pm改需求被程序员拍板砖的图,嘿!需求不稳定,就别动工,宁愿打酱油看博客。好了,啰嗦了这么多,有多少人会看到这里呢?希望以后有空回头来增加些内容。

    更多请参考:

    iOS应用架构谈

    说说ReactiveCocoa 2

    iOS 并发编程之 Operation Queues

    相关文章

      网友评论

      • _阿南_:看到是15年的文章,莫名的惊讶。为何现在推送一个15年的给我,是不是简书的算法有问题啊。
      • 程序员不务正业:尤其是业务为王的企业里,代码质量算个屁只要能work,可是好的程序员都是有尊严的......确实,某些传统企业宣称要互联网转型,从来就不关注架构层面,只要能跑起来,管你什么玩意
      • 新地球说着一口陌生腔调:怎样打成私有pod?
        另外大项目还是得用mvvm吗?
      • 三十六变:高手啊!
      • Airsola:嘿!需求不稳定,就别动工,宁愿打酱油看博客。 做到一半了,需求巨大变动几乎推倒重来,正在打酱油看博客中...
        程序员不务正业:我这打了半年酱油了,有点虚的慌...
      • 2dd6a71e901d:来抓一把腿毛
      • 7b802618b8b4:的却内容质量很高啊。 还得反思自己了
      • 微笑0619:我们是JointForce杰微刊。致力于为国内IT同胞贡献有价值的内容。
        您的《iOS大型项目开发漫谈》写的非常好。我们希望与您合作,支付给您一定酬劳,支持您的写作。
        不知您是否有意?
        感谢您抽时间阅读。
      • 大象飞:真正的大牛啊,求抱大腿!!!!!!
      • 剑伟大大:哥,apple自己的app NEWS你是逆向看到他们使用FMDB的?
      • dccd03f431e5:定时器崩溃,很明显你没有对self进行若引用。
        d17d6abe654a:@dccd03f431e5 是吧,那个倒计时那里应该若引用,不然会造成内存泄露?新人,求解释,谢谢
      • bcecc90a27c6:代码字体和颜色 界面很好看养眼,请问 怎么做的? 我那个xcode怎么设置 要加插件吗?!QQ864571195 坐等联系
      • laughMaker:目录结构里,有一个小写的paylibs,对于强迫症晚期程序员,简直不能忍啊
        CrespoXiao:@laughMaker 不好意思没注意,那个是小兄弟加的,已改。
      • 1150b290110c:希望大神能详细讲一下如何进行iOS多人合作开发。虽然文章中提到了私有pod,但我还是不太明白。我刚毕业就去了一家小公司,然后一直是一个人做app,不懂如何多人配合。现在老板让我带徒弟,我都不知道怎么让他加入到项目里,怎样把控他的代码。
      • ch32053:dispatch_once 试用中,初始化dispatch_once_t 加了 static 修饰就是整个app生命周期内只执行一次,不加 static就是 某对象生命周期内只执行一次!!!!
        CrespoXiao:@wangyangyang 是的,但是那样写很怪,xcode dispatch once code snippet就是带static。
      • Sakya666:淘宝App也有用到私有pod,功能业务模块过多时效率提高很多。
        Sakya666:@70aeb86f006f 要知道苹果自己的app NEWS用的就是FMDB啊,我还记得有人问facebook的工程师“为啥你们的app速度慢呢”,“because we use core data!”。 mark了 :smirk:
      • 土豆发芽:老师接受历史的审判,,呵呵,我听了心里一凉。。
      • Ljson:很实在的东西。
      • d14da8c11b06:写的相当不错
      • 我_阿元:看得很爽,很nice~
      • 梓华:说到类之间高度耦合。在MVC下,Controller变成了super man。不知道楼主怎么看待【设计模式】这东西。
        利炳根:@luohuaDev

        可以先以10行为单位,在类内把代码拆散,然后再移出去,易如反掌
      • 640263ced1b6:dispatch_source_t _timer要声明成属性,否在在ARC下就提前释放了,然后就没有然后了
      • e8e3934921b1: 亲,能不能好好写写穿行线程怎么创建的? 误导人啊
      • f6623db7c1a0:大神能不能给看看store是啥样子的
        CrespoXiao:@f6623db7c1a0 要等到国庆后才有空抱歉,晚上有事。
      • angelababa:有一个疑问:第4点中的串行线程,是因为在Task1中的dispatch_group_async调用dispatch_group_async(main),才使得这些任务是串行的吗?如果Task1和Task1都只是一个println,那么这些应该是并行的吧?
        ROB_YONG:原来是主队列。。。这是例子没举好的问题。dispatch_get_global_queue我一般就用作并发了。。
        angelababa:@華米24 我的意思就是这样,本来这是一个并发执行的。为什么串行要这么写呢
        华子小筑:@angelababa (1)是因为Task扔到主线程中才成为串行(2)直接打印是并发 直接运行代码会很清晰
      • 买了否冷_:和题目没有太大关系!!说了好多nice的技术点,没有说怎么把控大项目!
        CrespoXiao:@拉莫斯 最重要的就是高手带头,思想统一,说多了你们不爱听。对代码有追求,细心,凝聚力与士气也很重要。其他以后再补充吧,昨晚写得匆忙。
      • 管策:写的很好
      • MasonFu:有点文不对题,不过内容非常不错!
      • 滴滴时光:特别好

      本文标题:iOS大型项目开发漫谈

      本文链接:https://www.haomeiwen.com/subject/ccuxcttx.html