快速常量: 结构或枚举

我不确定哪一个更适合定义常量。结构体或枚举。一个结构将被复制每次我使用它还是没有?当我考虑使用 static let常量的结构时,我认为它会一直复制是没有意义的。但如果它不会复制,那么我拿什么都无所谓了?

选择结构体或枚举有什么好处?

Francescu 说用 structs。

Ray Wenderlich 说要用枚举,但我没有理由。

44842 次浏览

Both structs and enumerations work. As an example, both

struct PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

and

enum PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}

work and define a static property PhysicalConstants.speedOfLight.

Re: A struct will be copied every time i use it or not?

Both struct and enum are value types so that would apply to enumerations as well. But that is irrelevant here because you don't have to create a value at all: Static properties (also called type properties) are properties of the type itself, not of an instance of that type.

Re: What advantages has the choice of a struct or enum?

As mentioned in the linked-to article:

The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.

So for a structure,

let foo = PhysicalConstants()

creates a (useless) value of type PhysicalConstants, but for a case-less enumeration it fails to compile:

let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers

Using Xcode 7.3.1 and Swift 2.2

While I agree with Martin R, and the Ray Wenderlich style guide makes a good point that enums are better in almost all use cases due to it being a pure namespace, there is one place where using a struct trumps enums.

Switch statements

Let's start with the struct version:

struct StaticVars {
static let someString = "someString"
}


switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

Using a struct, this will match and print out Matched StaticVars.someString.

Now lets consider the caseless enum version (by only changing the keyword struct to enum):

enum StaticVars {
static let someString = "someString"
}


switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

You'll notice that you get a compile time error in the switch statement on the case StaticVars.someString: line. The error is Enum case 'someString' not found in type 'String'.

There's a pseudo-workaround by converting the static property to a closure that returns the type instead.

So you would change it like this:

enum StaticVars {
static let someString = { return "someString" }
}


switch "someString" {
case StaticVars.someString(): print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

Note the need for parentheses in the case statement because it's now a function.

The downside is that now that we've made it a function, it gets executed every time it's invoked. So if it's just a simple primitive type like String or Int, this isn't so bad. It's essentially a computed property. If it's a constant that needs to be computed and you only want to compute it once, consider computing it into a different property and returning that already computed value in the closure.

You could also override the default initializer with a private one, and then you'll get the same kind of compile time error goodness as with the caseless enum.

struct StaticVars {
static let someString = "someString"
private init() {}
}

But with this, you'd want to put the declaration of the struct in its own file, because if you declared it in the same file as, say, a View Controller class, that class's file would still be able to accidentally instantiate a useless instance of StaticVars, but outside the class's file it would work as intended. But it's your call.

Here's a short answer: Do your constants need to be unique? Then use an enum, which enforces this.

Want to use several different constants to contain the same value (often useful for clarity)? Then use a struct, which allows this.

One difference between the two is that structs can be instantiated where as enums cannot. So in most scenarios where you just need to use constants, it's probably best to use enums to avoid confusion.

For example:

struct Constants {
static let someValue = "someValue"
}


let _ = Constants()

The above code would still be valid.

If we use an enum:

enum Constants {
static let someValue = "someValue"
}


let _ = Constants() // error

The above code will be invalid and therefor avoid confusion.

In the Combine framework, Apple has chosen to prefer enums for namespaces.

enum Publishers

A namespace for types that serve as publishers.

enum Subscribers

A namespace for types that serve as subscribers.

enum Subscriptions

A namespace for symbols related to subscriptions.