背景

最近一直在团队推进关于iOS模块独立运行相关的事项,想把最近的一些想法和实施情况通过这篇文章做一个记录。

如果在一个项目中,某一块代码足够独立(功能、业务上),就会倾向于将他通过Cocoapods抽离为一个pods文件。通过一个podspec文件描述这个pod的信息。

最直接的方式就是把相关文件组织好迁移到一个目录下,通过podspec对源码位置,资源位置等信息的描述,完成一个最简单直白的pod创建。

并且在Podfile中通通过

pod 'XXX', :path => '~/dev/XXX'
复制代码

这种方式,集成到主工程进行开发。开发自测完成后通过私有repo发布,并且将Podfile中的指向版本

pod 'XXX', '1.0.0'
复制代码

然后我们就会说,抽离了一个库,项目的代码结构变得更合理更清晰了。

但这么做,在我看来跟在项目里直接用group把这些代码区别开来没什么区别,反而要修改的时候还要重新git pull、pod install变得更加麻烦了。

因为这么做被分离的代码无法独立运行,而且由于依赖不清晰,没办法共享给其他项目使用,导致这种分离方式是一种伪解耦,该有的益处没有展现出来,修改的时候反倒更加麻烦,这绝对不是我们想要的效果。

由此引出对模块独立运行

对iOS模块独立运行的思考

个人心目中完美的Pod应该在这四个维度上做到最好。

  • 依赖足够清晰 清楚的描述自己的依赖状况。说到这个不得不吐槽一个iOS项目与Cocoapods结合之后一个奇怪的现象,就是Pod如果在主工程中通过path引入的,那么,在不声明清楚自身以来的情况下,可以使用主工程内所有类,甚至是主工程的类,并且不会得到任何提示,这种情况很普遍,并且事后想要理清依赖的成本极高,这个Pod算是废了(没有了抽离Pod的意义了)。

  • 方便共享给其他项目使用 我的理解,抽离Pod的很大一个目的不就是为了共享吗?如果不是为了这个目的,其实没必要抽离Pod的,反而更加麻烦了,只跟一个项目绑死的Pod在我看来,完全没有必要做成Pod,项目里放在Group就可以了。而为了能达到这个目的需要克服很多的困难。

  • 方便快速修改验证 随着主工程越来越大,编译速度越来越慢,开发效率也在无形之中慢慢降低。Pod从主工程中脱离出来独立运行,单独编译,隔绝对主工程的依赖,完全自给自足,这样代码编译量就会大大降低,达到开发效能提高的目的。

  • 自身质量保障 在第三点的基础上,带上完全针对这个Pod的单元测试UI测试,完美颗粒化代码的同时,还能很好的保障自身的质量,并且清晰易维护,这块如果配合Xcode Server,会发挥强大的优势。

然而要做到这些维度上的最好,需要克服很多问题,接下来慢慢道来。

两种类型的代码

类似AFNetworking、SDWebImage这样的功能型代码,分离的一个只需要确保自己的依赖清晰,被依赖的时候使用方便就可以了。而对于偏业务型的代码就不那么容易了,通常会有界面,还会有各种业务带来的附加产物,例如打点,例如网络库各种规则。

因为功能型代码本身对于以上四点门槛不高,我就不展开讨论了,主要还是展开说一下业务型代码。

业务型代码独立运行的看法

推进业务型代码独立运行过程中遇到的一些问题列举:

  1. 业务代码需要用到 打点、网络等基本能力,背后每个能力都可能牵扯出一堆间接依赖,但这些依赖跟这个Pod本身没任何屁关系,同时还会是不是的出现间接依赖不明确导致的编译报错问题。

  2. 作为已经是独立可运行的Pod了,界面什么的都自己hold了,那么它一定还需要跟其他本身之外的几面进行交互,举个例子,一个Product的Pod,需要跳转到Order中的一个界面,或者Chat中的一个界面,而这个界面在代码层面根本不存在,要如何处置。

  3. 之前也提到了,一个依赖清晰的独立运行Pod如果被不小心path方式开发了一次,那么这个Pod会慢慢变废,下次运行可能就不能运行了,所以还要想办法要怎么不被path依赖。

  4. 还有一个比较头疼的问题是,随着业务迭代,某个冷门的独立运行Pod并没有跟上脚步,其直接依赖的功能库在主工程都更新了,但它却全然不知,难道还要一个一个校对吗?

  5. 解决了基础能力的间接依赖,各种必要的直接依赖的间接依赖也会出现不明确而出现的编译失败问题,需要解决,确保只有代码api需要更新时才会编译不过,才是最爽的开发流程。

思考实施解决之道

实施过程中开发了两套工具来解决。均未开源,外网勿搜。下面介绍详细思路。

脚手架工具 - gearmaker

避免被path模式开发

好不容建了一个独立运行Pod,要是不小心被不明真相的同学用path开发了就糟了。如何避免被path模式开发呢?如果是源码模式下,其实是做不到的。所以我们把每个独立运行的Pod的产物定位二进制库,静态动态都可,podspec层面就不允许指向源码,想要修改源码,只能通过独立运行的工程进行修改。

具体操作这里不展开了,如果podspec指向的是静态库,而没有源码指向则这个Pod理论上不可能不可被path模式开发。

而如果通过Cocoapods 官方建议的 pod lib create 方式创建,则Pod代码会存在于 Development Pods 下,而必须在podspec中指定源码路径,因此我修改了 pod lib create 的脚手架模板,将Pod代码直接放入项目的一个Group中,而Group对应产物是一个framework,podspec直接指向podspec,外加Universal打包脚本就可以啦。

有同学有疑问了,那源码调试怎么办呢?这个不用担心,既然Pod已经可以独立运行,有什么问题需要调试,是都可以在Pod工程中进行数据Mock来还原问题的,所以主工程只是用来集成,不需要考虑调试问题。

即便是特别特殊的情况,只在主工程能还原,那临时加一下podspec指向本地path做一下debug也是可以的。

版本仲裁 & 保鲜

Pod独立运行工程的一大诟病就是时间一长,工程就无法编译通过运行了,并且哪些依赖需要更新,需要详细对比,成本非常高,很多遇到这样情况就会放弃Pod工程。

为此gearmaker中集成了版本仲裁能力,通过hook pod命令,在pod install之前,计算出指定客户端主工程最新的依赖全集,在pod install时,在这个全集中找到仲裁版本来使用。

这样一来,pod install后的Pod工程所有依赖必然与主工程一致,只需要修改因为依赖更新带来的相关api变更即可通过编译正常运行,确保Pod工程不会腐败。

依赖切断 服务提供组件 - ServiceProvider

所有Pod只需要直接依赖ServiceProvider,由ServiceProvider来统一提供服务能力,包括Pod工程本身需要的任何能力。比如,路由、打点、网络、从另一个模块获取数据,获取一个View对象等,均不需要依赖其他库,直接从ServiceProvider中通过内置的protocol来获得,并使用。

提供能力的一方对预先放置在ServiceProvider中的protocol进行功能实现,通过以下方式将自身的能力注册入ServiceProvider,即可为其他提供能力,而不需要依赖。

注册服务

[ServiceProvider registService:[XMUserTrack class] withProtocol:@protocol(UserTrack)];
复制代码

获取服务

id<UserTrack> ut = [ServiceProvider serviceWithProtocol:@protocol(UserTrack)];
复制代码

使用体验

对于新的Pod创建,直接使用以下命令创建:

gearmaker <PodName>
复制代码

根据命令行提示进行创建即可。创建完成的脚手架直接提供了ServiceProvider,开发同学直接从ServiceProvider中获取服务进行Pod开发,开发完成后通过Universal脚本生成framework上传到私有repo中定版本即可直接使用。

深入谈一谈iOS模块独立运行相关推荐

  1. 初识ABP vNext(12):模块的独立运行与托管

    点击上方蓝字"小黑在哪里"关注我吧 模块运行 动态 C# API 客户端 前言 很久没更新这个系列...之前的章节中讲到ABP的模块是可以独立运行的,但是没有介绍具体怎么操作,本篇 ...

  2. 浅谈STM32的DMA模块的使用

    浅谈STM32的DMA模块的使用 转自:http://blog.ednchina.com/jack_chang/123085/message.aspx http://article.ednchina. ...

  3. python sys模块作用_浅谈Python中的模块

    模块 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很多编程语言都采用这种组织代码的方式.在Python中,一个.py文件就称之为一个模块(Mod ...

  4. 简谈WP,IOS,Android智能手机OS

    什么是智能手机? 相信到现在这个已经是傻瓜到不能再傻瓜的问题了 智能手机都不懂? 那你活着还有什么意思= = 但是为了谈论今天的三大主角:wp,ios,android 不得不回答一下这个笨笨的问题 如 ...

  5. ant man 什么意思_浅谈为什么很多蓝牙模块厂家选择nRF52832?

    浅谈为什么很多蓝牙模块厂家选择nRF52832? 现在蓝牙低功耗(BLE)SOC作为新一代蓝牙,以其低功耗的优势,正凸显出强大的市场竞争力,而其中Nordic公司的nRF52832这一款低功耗蓝牙芯片 ...

  6. 趣谈网络协议-第二模块-底层网络知识详解:4陌生的数据中心2CDN和数据中心

    趣谈网络协议-第二模块-底层网络知识详解:4陌生的数据中心2CDN和数据中心 1:CDN:你去小卖部取过快递么? 使用"中间仓库"来优化 网络中的"就近配送" ...

  7. 浅谈 unix, linux, ios, android 区别和联系

    浅谈 unix, linux, ios, android 区别和联系 网上的答案并不是很好,便从网上整理的相对专业的问答. 1.UNIX 和 Linux UNIX 操作系统(尤尼斯), 是一个强大的多 ...

  8. 趣谈网络协议-第二模块-底层网络知识详解:2最重要的传输层

    趣谈网络协议-第二模块-底层网络知识详解:2最重要的传输层 1:第10讲 | UDP协议:因性善而简单,难免碰到"城会玩" TCP 和 UDP 有哪些区别? UDP 包头是什么样的 ...

  9. 关于计算机运行管理模式,浅谈学校计算机机房管理及维护运行模式.docx

    浅谈学校计算机机房管理及维护运行模式 摘要:计算机辅助教学在学校的教学体系中占有重要地 位,计算机机房是学校教学和学生实践学习的重要学习场 所.合理的机房管理与维护模式,是提高学校教学质量和培 养学生 ...

最新文章

  1. OpenCV+python:模糊操作
  2. 「模型解读」GoogLeNet中的inception结构,你看懂了吗
  3. python 类中方法的动态特性
  4. 印发 指南 通知_通知设计的综合指南
  5. 1259:【例9.3】求最长不下降序列
  6. 图论 —— AOE 网与关键路径
  7. java冒泡排序 快速排序_Java必备-冒泡排序,选择排序,快速排序(纯代码实现)
  8. Ajax模拟Form表单提交,含多种数据上传
  9. 交叉连接、内连接和外连接的区别及使用方式
  10. Kail linux中无法定位软件包
  11. 深入浅出VA函数的使用技巧
  12. javascript(String, Array, Math, Date, Object)方法整理
  13. 强化学习之AC系列算法(AC、A2C、A3C)
  14. 集合中某几个数字之和等于一个固定值 java
  15. 网络游戏前后端时间同步
  16. 人效提高350%,基于KICP搭建的营销套电客服机器人,让欧派家居赢在起点
  17. Head First Java资源
  18. HDU 1415(Jugs)
  19. 读:Multi-scale pulmonary nodule classification with deep feature fusion via residual network
  20. 港科夜闻|中科院院士、深圳湾实验室常务副主任(主持工作)吴云东教授一行莅临香港科大(广州)参观访问...

热门文章

  1. Python 查看pip安装的包的位置(查看pip安装包的路径)
  2. MongoDB未授权访问漏洞记录(端口:27017,37017)
  3. 4. OD-去除烦人的nag窗口(去除提醒用户购买正版的警告窗口)
  4. S3C2440 偷学
  5. STM32 初学不知道外设对应的APB1还是APB2
  6. 如何:添加缺少的 ContentPlaceHolder
  7. OpenSSL的托管项目
  8. ubuntu18.04新安装时Unable to locate package问题
  9. 65岁的编程语言重回Top 20,65岁的程序员还没退休吗?
  10. 如何成为一名糟糕的程序员?