博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS - Swift Enumerations or how to annoy Tom
阅读量:7151 次
发布时间:2019-06-29

本文共 7534 字,大约阅读时间需要 25 分钟。

 

本文转载自 

 

In the dark fetid implementation mists behind the slick city Swift streets lies a secret world where enumerations are merely ints with pretensions. In more objective terms, Swift enums provide a complete and finite ordered listing of the possible members in a fixed collection. They basically come in three flavors.

 

Basic EnumerationsFirst, there’s a common variety of “related things” that form a family.

enum Coin {case Heads, Tails}

These basic enumerations provide a fixed vocabulary about possible states you may encounter (if rollValue == .Heads or if collection.contains(.Tails)). Collections with enumerations can contain repetitions. For example, [.Tails, .Tails, .Heads] represents a series of enumeration states, perhaps detailing the history of coin tosses.

Avoid basic enumerations for bit flags as there’s a specific  for flags.

Fixed Values. A second flavor of enums offer an associated a raw value. The following example uses natural ordering, starting with 1. Tails’ rawValue is 2.

enum Coin: Int {case Heads = 1, Tails}

These values can be continuous, as in this example, or discrete {case Heads = 5, Tails = 23} but the type, which is declared after the enumeration name, is always homogenous.

You can use other types, such as Strings, but there’s always a one-to-one correspondence between the enumeration and its value. So a .King enumeration may always equate to 12 or “King”. Think of these as a look-up table.

Associated PayloadsAnd there’s the kind that packs payloads. The most commonly used implementation of these is optionals (case .None, case .Some(T)) but you can build your own as well. Cases can use any types, and those types may include tuples.

enum Coin {case Heads(NSDate, Bool, String); case Tails}

The Dark Underbelly of the Enum

To better understand enumerations, it helps to poke at them with a sharp stick. For obvious reasons, don’t use the following material for production code. Or really for any code. That said, I found this exercise extremely valuable for understanding how enums work.

Enums are typically one byte long.

sizeof(Coin); // 1

If you want to get very very silly, you can build an enumeration with , in which case the enum takes up 2 or more bytes depending on the minimum bit count needed. If you’re building enumerations with more than 256 cases, you probably should reconsider why you’re using enumerations.

Basic enumerations (the first two cases) are Hashable, that is, they provide an integer hashValue that is unique to each value. Unsurprisingly, the hash values for enumerations start at zero and increase monotonically. (Yes, this is an implementation detail. Alarm bell. Warning blare.)

enum Planets {case Mercury, Venus, Earth, Mars, Jupiter, Saturn, Neptune, Uranus, Pluto} print(Planets.Mars.hashValue) // 3print(Planets.Mercury.hashValue) // 0

Enumerations with basic associated values are also raw representable. The rawValue follows from whatever default or explicit assignment you’ve made. All raw values are of the same type, in this case Int:

enum Foo : Int {case i = 1, j = 5, k = 9}Foo.j.hashValue // 1Foo.j.rawValue // 5

Moving Forward

So given these details of how enumerations work:

  • How do you create instances from rawValues?
  • How do you create instances from hashValues?
  • How do you query an enumeration about its members?

I warn you in the strongest possible terms against continuing to read. Tom does not approve.

Creating instances from Raw Values

Okay, I lied. This one, he does approve of. That’s because it’s legal.

enum Foo : Int {case i = 1, j = 5, k = 9}Foo(rawValue: 5) // .j

You create instances using the built-in constructor, supplying the raw value.

Enhancing Enums

Now we wander into the shadow of the valley of doom, so you should start to fear some evil. Swift is no longer with you, and unsafe bitcasts lie ahead. The goal is to create this protocol, with default implementations that supplies the following features to all enumerations of the first two types.

public protocol EnumConvertible: Hashable {    init?(hashValue hash: Int)    static func countMembers() -> Int    static func members() -> [Self]}

Building Enums from Hash Values

Since it’s a given that an enumeration is basically an Int8 that stores the hash value, you can build a really simple initializer. Just cast the Int8 that’s initialized with a hash value to the enumeration type.

let member = unsafeBitCast(UInt8(index), Self.self)

This line doesn’t check for safe values, so you probably want to use some sort of check that the value is within the membership limit, and create a failable initializer instead.

internal static func fromHash(    hashValue index: Int) -> Self {    let member = unsafeBitCast(UInt8(index), Self.self)    return member}public init?(hashValue hash: Int) {    if hash >= Self.countMembers() {return nil}    self = Self.fromHash(hashValue: hash)}

Once added to the protocol, you can construct an instance from its hash value (countable, starting with 0) and look up its raw value:

Foo(hashValue: 1)!.rawValue // 5

Boom done. And Tom turns away, disapproval writ large upon his face.

Counting Members

The hash-value-based init depends on there being some way to count enumeration members. If you know you’ll always deal with one-byte enumerations, this is super easy. Adding support for two bytes isn’t much harder.

static public func countMembers() -> Int {    let byteCount = sizeof(self)    if byteCount == 0 {return 1}    if byteCount byteCount > 2 {        fatalError("Unable to process enumeration")}    let singleByte = byteCount == 1    let minValue = singleByte ? 2 : 257    let maxValue = singleByte ? 2 << 8 : 2 << 16    for hashIndex in minValue..

This approach uses a simple iteration to construct values until a hashValue look-up fails. It’s pretty brain dead although it knows that 2-byte enums cannot contain fewer than 256 values.

Unfortunately, protocol implementation doesn’t allow you to create storage so you end up re-computing this value all the time (or would if you used this, which you won’t because Tom would not approve).

Enumerating Enumerations

The final goal lies in creating a collection that allows you to enumerate through your enumerations to cover all available cases. For that, you need to return all members.

static public func members() -> [Self] {    var enumerationMembers = [Self]()    let singleByte = sizeof(self) == 1    for index in 0..

As with the membership count, this is something that would benefit either from being built-in (well, of course), or from implementation that prevents it being computed more than once.

Once you add this, you can perform tasks like “show me a function as it relates to each member of an enumeration”. Although actual sequencing is an illusion — enumeration members may not be built upon any intrinsic sequence semantics — it can be super handy to be able to access items in this way.

Wrap-Up

You can see an example of why this function would be particularly helpful in this gist, which I wrote in response to a post on devforums. Someone was looking for a probability-weighted enum and it was their post that led me to start exploring this whole question.

I gisted my . It is based on a far less elegant solution for collecting members but it showcases why the use-case is valid and compelling.

The entire protocol discussed in this post is at  and awaits your feedback, insight, and suggestions. Please tweet or leave comments here because Github doesn’t notify by email.

Finally. Sorry, Tom.

转载于:https://www.cnblogs.com/QianChia/p/8219602.html

你可能感兴趣的文章
我的第一次面试经历
查看>>
表格行mouse经过时高亮显示
查看>>
【GoLang】GoLang 官方 对 error 处理的意见
查看>>
MVC 5使用ViewData(对象)显示数据
查看>>
错误处理与调试 (14 章 )
查看>>
ytu 1985:C语言实验——保留字母(水题)
查看>>
java常见错误--Access restriction: The type BASE64Encoder
查看>>
Mybatis 对象关系映射之一对多 and 多对多 关系
查看>>
周下载量过 200 万的 npm 包被注入恶意代码,Vue等 项目恐受影响
查看>>
[译] 高性能 Java 缓存库 — Caffeine
查看>>
[译] 用 API 请求制作赏心悦目的 UX
查看>>
# 闲谈 Objective-C 与 Swift
查看>>
iOS购物车商品加减逻辑(附赠block块使用)
查看>>
通用 Gradle打包混淆Jar, 合并第三方引用的Jar (Gradle 3.0)
查看>>
python性能优化之函数执行时间分析
查看>>
原生JS实现最简单的图片懒加载
查看>>
文本分类中的一些小问题
查看>>
【后知后觉系列】css position: sticky 属性以及某些场景的使用
查看>>
基于Flutter Canvas的飞机大战(二)
查看>>
为什么局部下降最快的方向就是梯度的负方向?
查看>>