TinySolution:解决enum的限制

Galvin Li
5 min readJan 13, 2019

--

English version also available: TinySolution: Resolve the restrictions of enum

Swift的enum相比Objective-C来说已经强大了很多,我们可以创建基于各种类型、带有不同参数的case值,在一些不同状态或对象具有截然不同的数据类型的情况下非常有用,例如我们常用的Result类型。

enum虽然强大,但也存在一些限制,举个例子,我们经常需要在程序画两种线条,实线和虚线,可以指定线宽,这时候我们可以用enum实现这个工具类。

现实需求总不是那么简单

虚线其实除了需要指定线宽之外,还需要支持定义虚线段的宽度和间隔位置,当然我们可以把Line改成以下格式:

问题确实解决了,但大部分的虚线都使用同样的线段宽和间隔,我们并不想每个地方都手动传一次相同的值,所以我们尝试使用默认参数:

很不幸编译器告诉我们enum不支持默认参数,但我们从Objective-C就承受着这个限制,对于如何实现默认参数的效果也驾轻就熟了,于是我们改成这个模样:

编译器告诉我们ok,三种case对象的创建也都成功。所以问题解决了?至少看起来确实如此。

奇怪的表现

当你开始写switch方法的时候,怪异的情况就发生了。例如我添加一个output()的方法,出现了一个警告:

前面一个case dash(...)包含了后面一个的所有情况了,考虑到前一个参数少一点,我们交换一下:

然而情况还是一样,看来两个case dash(...)对于编译器来说都是一样的,因此我们无法找出准确的case

如果这就是问题的全部,那这种实现还是有作用,但事实并非如此。让我们基于交换后那个Line类型编写几个测试:

可以看到Line.dash(width:1)被判断成default,不属于Line.dash类型,因此这种写法已经属于一个异常的写法了,同名的case后者会把前者的功能覆盖掉,前者仅有创建对象的功能,而且switch也无法判断,基本属于不可用的状态。

这或许属于Swift的bug?知道的可以告诉我一下。

解决方案

既然是TinySolution,我们当然要针对问题给出解决方法。简单实现我们可以手动创建一个static func dash替代case dash(width: Float)

这样我们就能正常输出和判断我们的Line对象:

但把自动生成和手动添加的初始化方法混合使用始终有点不纯粹,特别是如果初始化的时候需要执行某些配置,两者的灵活性不一致。因为如果我们尝试重写自动生成的静态方法,会出现重定义的编译错误:

更好的解决方案

当然既然我们能定义一个初始化方法,我们可以把case名称改掉,然后自行创建所有初始化方法,但这样就有两套初始化方法混杂,可能存在不一样的行为,而且这个对象还是enum,当我们switch的时候就会看到一套跟我们日常使用完全不一致的case,我觉得这样并不是一种好的方案,所以我推荐另一种更加清晰的实现:

首先我们用一个struct Line包裹起原来的enum Line,为了命名不要冲突我们把enum Line的名称改成enum Content,然后struct持有对应的enum对象,把原有enum的方法抽取到struct层,再添加静态创建方法去实现enum的创建效果,因为是静态方法所以我们的默认参数又可以大派用场了。这时候我们依然不用修改之前enum对象的创建代码,直接就可以正常使用。

  • 本文用到的代码均可以在Github项目里面找到。
  • 如果你对文中的内容有疑问或者建议,欢迎留言讨论。
  • 如果你觉得文中内容有价值,请转发让更多人可以看到。
  • 如果你喜欢这类型内容,欢迎follow我的MediumTwitter,我会持续发布更多有用内容给大家。

--

--

Galvin Li

A Tiny iOS developer who love to solve problems and make things better.