107 – Category在代码结构上的应用

27. 六月 2019 iOS 0

很想写一篇关于架构方面的文章,但是还是觉得从我重构的一份代码来梳理下我对于 解耦 的一些理解。

在这之前,我想先说一下 category ,以及我对category的一些理解。
我做过一段时间的Unity,Unity是用C#写脚本文件,然后将 这些脚本挂载到Object上,这些物体就有了脚本所带来的特性
这对于我,理解起来,在iOS里就是category。(当然脚本还有很多其它特性,不再细细讨论)
这也是我在iOS里会使用很多category的原因。

抽象公共功能 – 举一些具体的例子

比如,社交app里会有很多按钮,这些按钮产品在设计的时候,想要在button上显示一个loading来表示点击后和网络在通信。
怎么做?

  1. 写一个父类,然后继承?
  2. 写一个category(UIButton + LoadingInside),需要的button直接挂载就好了?

我认为第一种做法,太重了,重在我需要改变类本身,举个例子,

  1. 我如果不是用的UIButton,而是UIButton的子类,难道要多继承么?
  2. 如果后面交互改了,点赞按钮要变出一朵花或者不需要任何操作,按照思路,需要我们重新写类?

我选择把能力写入category,是因为

  1. 轻量。需要某种category能力,引入,就拥有了在category的定义能力
  2. 独立的特性。同一种UIButton,aBtn可以有,bBtn可以没有。
  3. 可以自由扩大的能力范围。举个例子,如果你自定的UIView,现在也需要loading,那么一样,我们将UIButton扩大为UIView + LoadingInside就可以了
  4. 独立的功能定义。如果我们按照这种思路来加入能力,那么新加的能力都可以是一个独立的category,这样我们可以拼凑出更多种的能力组合,并且不会耦合

不知道到此,对category的理解有没有更深一步,如果还没有感觉,下面我会用一个项目中的例子,来进一步解析。

解耦 – 使用category进行单例文件进行拆解

首先,给大家讲述一下,我要拆解的这个文件功能:

  • search,发现局域网内电视设备 -> 搜索(局域网广播)
  • connect,连接,保持心跳 -> 通过UDPSocket实现,连接(connecting,获取到ip+port)),保持心跳(connected)
  • dataSend & dataRecive,与电视交换信息(发送指令,接收指令)-> 通过sessionType判断(search,connect,control,game,pic,video,setting)

原本这个大文件是单例模式,[TVConnectManager shareManager],结构大概如下,还有一些很杂七杂八的,例如数据打包之类的函数等等,差不多有2000行代码左右

我们从大概结构就可以看出,如果电视端的业务增加,添加一个sessionType,那么我们就需要再写一个else if
一旦逻辑有些问题,那么就需要在庞大的if else里寻找寻问题。
改!
改动的原则:

  1. 父类为子类提供最基础的功能
  2. 子类为调用者提供最直接的功能
  3. 各个业务只关心自己的业务(遥控器业务只关心对电视的control)

拆!
拆的方式:

  1. 父类只有基础操作:初始化socket,搜索局域网的电视,提供连接、维持心跳、收发消息的功能
  2. 子类开放的操作:初始化:通过父类的类方法,获取到所有局域网电视信息,通过信息生成未连接子类,连接成功后返回一个已连接的子类。子类可以自发维持心跳,收发消息。
  3. 子类各个扩展的业务:通过接收消息中的sessionType生成不同的SEL,然后通过performSelector来将数据分发给不同的业务模块。

实现!
父类:基本上使用类方法就够了。

子类:其实什么都不用做,保存了当前的连接信息,发消息的基础功能父类已经实现

业务:真正的对于业务层的同学,就只用关心这个类

这么做,业务层端就只需要引入头文件XXConnecttedTVManager+MediaSessionType.h,每个业务的 dataManager 处理在 category 里获取到的数据就可以了。

如果有新的业务需要添加一些功能,只用多加一个category文件,写入自己的处理代码。同样,如果不再需要某部分代码,直接删除文件就可以了,不会影响基本功能。

就如同脚本挂载在object上,获取到某种功能;扔掉脚本,就失去了这种功能。

总结

还是需要真正理解架构,比如MVC和MVVM、MVP这种经典架构,会提供给我们更加完整和清晰的解耦方法。
category如果按照我的结构拆分,会导致文件数增加,因为在app启动时,call_class_loads完成之后调用call_category_loads。文件多的话,加载耗时(即将写一篇文章:iOS启动优化)。