What Is Swift Enum Byte Representation

What is Swift enum byte representation?

The Swift ABI is still work in progress (expected to be fixed with Swift 4). The way enums are represented in memory is described here.

Your case is a "c-like enum" because is has...

  • two or more cases
  • no associated values

Quoting the ABI document:

the enum is laid out as an integer tag with the minimal number of bits to contain all of the cases. [...] The cases are assigned tag values in declaration order.

The crucial information here is the "minimal number of bits". This means that (for your case) instances should fit into two bits (as you have three cases). The rawValue 0x10 would need five bits—which would be in conflict with the ABI.

The compiler probably uses static tables to convert between instances of Enum and their rawValue (and back).

Here's an example that highlights this characteristic of the ABI:

enum Enum: UInt32
{
case A = 0
case B = 0xffffffff // value that does not fit into one byte
}

assert(sizeof(Enum.self) == 1)

Swift enum size when associated value is a reference type

From Type Layout: Single-Payload Enums:

If the data type's binary representation has extra inhabitants, that is, bit patterns with the size and alignment of the type but which do not form valid values of that type, they are used to represent the no-data cases, with extra inhabitants in order of ascending numeric value matching no-data cases in declaration order.

Your example with more cases:

enum Opt<T> {
case a, b, c, d, e, f, g, h, i, j, k
case l, m, n, o, p, q, r, s, t, u, v
case some(T)
}

class Person {
var name: String
init(name: String) { self.name = name }
}

print(unsafeBitCast(Opt<Person>.a, to: UnsafeRawPointer.self))
// 0x0000000000000000

print(unsafeBitCast(Opt<Person>.b, to: UnsafeRawPointer.self))
// 0x0000000000000002

print(unsafeBitCast(Opt<Person>.v, to: UnsafeRawPointer.self))
// 0x000000000000002a

let p = Person(name: "Bob")
print(unsafeBitCast(Opt.some(p), to: UnsafeRawPointer.self))
// 0x00006030000435d0

Apparently, 0x0, 0x2, ..., 0x2a are invalid bit patterns for a pointer, and therefore used for the additional cases.

The precise algorithm seems to be undocumented, one probably would have to inspect the Swift compiler source code.

Declaring and using a bit field enum in Swift

Updated for Swift 2/3

Since swift 2, a new solution has been added as "raw option set" (see: Documentation), which is essentially the same as my original response, but using structs that allow arbitrary values.

This is the original question rewritten as an OptionSet:

struct MyOptions: OptionSet
{
let rawValue: UInt8

static let One = MyOptions(rawValue: 0x01)
static let Two = MyOptions(rawValue: 0x02)
static let Four = MyOptions(rawValue: 0x04)
static let Eight = MyOptions(rawValue: 0x08)
}

let m1 : MyOptions = .One

let combined : MyOptions = [MyOptions.One, MyOptions.Four]

Combining with new values can be done exactly as Set operations (thus the OptionSet part), .union, likewise:

m1.union(.Four).rawValue // Produces 5

Same as doing One | Four in its C-equivalent. As for One & Mask != 0, can be specified as a non-empty intersection

// Equivalent of A & B != 0
if !m1.intersection(combined).isEmpty
{
// m1 belongs is in combined
}

Weirdly enough, most of the C-style bitwise enums have been converted to their OptionSet equivalent on Swift 3, but Calendar.Compontents does away with a Set<Enum>:

let compontentKeys : Set<Calendar.Component> = [.day, .month, .year]

Whereas the original NSCalendarUnit was a bitwise enum. So both approaches are usable (thus the original response remains valid)

Original Response

I think the best thing to do, is to simply avoid the bitmask syntax until the Swift devs figure out a better way.

Most of the times, the problem can be solved using an enum and and a Set

enum Options
{
case A, B, C, D
}

var options = Set<Options>(arrayLiteral: .A, .D)

An and check (options & .A) could be defined as:

options.contains(.A)

Or for multiple "flags" could be:

options.isSupersetOf(Set<Options>(arrayLiteral: .A, .D))

Adding new flags (options |= .C):

options.insert(.C)

This also allows for using all the new stuff with enums: custom types, pattern matching with switch case, etc.

Of course, it doesn't have the efficiency of bitwise operations, nor it would be compatible with low level things (like sending bluetooth commands), but it's useful for UI elements that the overhead of the UI outweighs the cost of the Set operations.

What is the equivalent data type for byte in iOS swift?

The data type is Unsigned Int 8 (UInt8).

var byte:UInt8 = 0xAF

For a string of bytes:

var bytes:[UInt8] = [0xAF,0xAB]

For the bytes from data:

var data = Data()
var bytes = data.bytes //[UInt8]

Raw value for enum case must be a literal

That's because 1 << 0 isn't a literal. You can use a binary literal which is a literal and is allowed there:

enum GestureDirection:UInt {
case Up = 0b000
case Down = 0b001
case Left = 0b010
case Right = 0b100
}

Enums only support raw-value-literals which are either numeric-literal (numbers) string-literal­ (strings) or boolean-literal­ (bool) per the language grammar.

Instead as a workaround and still give a good indication of what you're doing.

How to enumerate an enum with String type?

Swift 4.2+

Starting with Swift 4.2 (with Xcode 10), just add protocol conformance to CaseIterable to benefit from allCases. To add this protocol conformance, you simply need to write somewhere:

extension Suit: CaseIterable {}

If the enum is your own, you may specify the conformance directly in the declaration:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

Then the following code will print all possible values:

Suit.allCases.forEach {
print($0.rawValue)
}


Compatibility with earlier Swift versions (3.x and 4.x)

If you need to support Swift 3.x or 4.0, you may mimic the Swift 4.2 implementation by adding the following code:

#if !swift(>=4.2)
public protocol CaseIterable {
associatedtype AllCases: Collection where AllCases.Element == Self
static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
static var allCases: [Self] {
return [Self](AnySequence { () -> AnyIterator<Self> in
var raw = 0
var first: Self?
return AnyIterator {
let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
if raw == 0 {
first = current
} else if current == first {
return nil
}
raw += 1
return current
}
})
}
}
#endif

Swift: Convert enum value to String?

Not sure in which Swift version this feature was added, but right now (Swift 2.1) you only need this code:

enum Audience : String {
case public
case friends
case private
}

let audience = Audience.public.rawValue // "public"

When strings are used for raw values, the implicit value for each case
is the text of that case’s name.

[...]

enum CompassPoint : String {
case north, south, east, west
}

In the example above, CompassPoint.south has an implicit raw value of
"south", and so on.

You access the raw value of an enumeration case with its rawValue
property:

let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"

Source.



Related Topics



Leave a reply



Submit