[swift] Why Choose Struct Over Class?

Since struct instances are allocated on stack, and class instances are allocated on heap, structs can sometimes be drastically faster.

However, you should always measure it yourself and decide based on your unique use case.

Consider the following example, which demonstrates 2 strategies of wrapping Int data type using struct and class. I am using 10 repeated values are to better reflect real world, where you have multiple fields.

class Int10Class {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

struct Int10Struct {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

func + (x: Int10Class, y: Int10Class) -> Int10Class {
    return IntClass(x.value + y.value)
}

func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
    return IntStruct(x.value + y.value)
}

Performance is measured using

// Measure Int10Class
measure("class (10 fields)") {
    var x = Int10Class(0)
    for _ in 1...10000000 {
        x = x + Int10Class(1)
    }
}

// Measure Int10Struct
measure("struct (10 fields)") {
    var y = Int10Struct(0)
    for _ in 1...10000000 {
        y = y + Int10Struct(1)
    }
}

func measure(name: String, @noescape block: () -> ()) {
    let t0 = CACurrentMediaTime()

    block()

    let dt = CACurrentMediaTime() - t0
    print("\(name) -> \(dt)")
}

Code can be found at https://github.com/knguyen2708/StructVsClassPerformance

UPDATE (27 Mar 2018):

As of Swift 4.0, Xcode 9.2, running Release build on iPhone 6S, iOS 11.2.6, Swift Compiler setting is -O -whole-module-optimization:

  • class version took 2.06 seconds
  • struct version took 4.17e-08 seconds (50,000,000 times faster)

(I no longer average multiple runs, as variances are very small, under 5%)

Note: the difference is a lot less dramatic without whole module optimization. I'd be glad if someone can point out what the flag actually does.


UPDATE (7 May 2016):

As of Swift 2.2.1, Xcode 7.3, running Release build on iPhone 6S, iOS 9.3.1, averaged over 5 runs, Swift Compiler setting is -O -whole-module-optimization:

  • class version took 2.159942142s
  • struct version took 5.83E-08s (37,000,000 times faster)

Note: as someone mentioned that in real-world scenarios, there will be likely more than 1 field in a struct, I have added tests for structs/classes with 10 fields instead of 1. Surprisingly, results don't vary much.


ORIGINAL RESULTS (1 June 2014):

(Ran on struct/class with 1 field, not 10)

As of Swift 1.2, Xcode 6.3.2, running Release build on iPhone 5S, iOS 8.3, averaged over 5 runs

  • class version took 9.788332333s
  • struct version took 0.010532942s (900 times faster)

OLD RESULTS (from unknown time)

(Ran on struct/class with 1 field, not 10)

With release build on my MacBook Pro:

  • The class version took 1.10082 sec
  • The struct version took 0.02324 sec (50 times faster)

Examples related to swift

Make a VStack fill the width of the screen in SwiftUI Xcode 10.2.1 Command PhaseScriptExecution failed with a nonzero exit code Command CompileSwift failed with a nonzero exit code in Xcode 10 Convert Json string to Json object in Swift 4 iOS Swift - Get the Current Local Time and Date Timestamp Xcode 9 Swift Language Version (SWIFT_VERSION) How do I use Safe Area Layout programmatically? How can I use String substring in Swift 4? 'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator Safe Area of Xcode 9 The use of Swift 3 @objc inference in Swift 4 mode is deprecated?

Examples related to class

String method cannot be found in a main class method Class constructor type in typescript? ReactJS - Call One Component Method From Another Component How do I declare a model class in my Angular 2 component using TypeScript? When to use Interface and Model in TypeScript / Angular Swift Error: Editor placeholder in source file Declaring static constants in ES6 classes? Creating a static class with no instances In R, dealing with Error: ggplot2 doesn't know how to deal with data of class numeric Static vs class functions/variables in Swift classes?

Examples related to struct

How to search for an element in a golang slice "error: assignment to expression with array type error" when I assign a struct field (C) How to set default values in Go structs How to check for an empty struct? error: expected primary-expression before ')' token (C) Init array of structs in Go How to print struct variables in console? Why Choose Struct Over Class? How to return a struct from a function in C++? Initializing array of structures

Examples related to design-principles

Why Choose Struct Over Class? What is an example of the Liskov Substitution Principle?