Why Choose Struct Over Class?

前端 未结 16 2090

Playing around with Swift, coming from a Java background, why would you want to choose a Struct instead of a Class? Seems like they are the same thing, with a Struct offeri

相关标签:
16条回答
  • 2020-11-22 06:00

    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)
    0 讨论(0)
  • 2020-11-22 06:03

    Assuming that we know Struct is a value type and Class is a reference type.

    If you don't know what a value type and a reference type are then see What's the difference between passing by reference vs. passing by value?

    Based on mikeash's post:

    ... Let's look at some extreme, obvious examples first. Integers are obviously copyable. They should be value types. Network sockets can't be sensibly copied. They should be reference types. Points, as in x, y pairs, are copyable. They should be value types. A controller that represents a disk can't be sensibly copied. That should be a reference type.

    Some types can be copied but it may not be something you want to happen all the time. This suggests that they should be reference types. For example, a button on the screen can conceptually be copied. The copy will not be quite identical to the original. A click on the copy will not activate the original. The copy will not occupy the same location on the screen. If you pass the button around or put it into a new variable you'll probably want to refer to the original button, and you'd only want to make a copy when it's explicitly requested. That means that your button type should be a reference type.

    View and window controllers are a similar example. They might be copyable, conceivably, but it's almost never what you'd want to do. They should be reference types.

    What about model types? You might have a User type representing a user on your system, or a Crime type representing an action taken by a User. These are pretty copyable, so they should probably be value types. However, you probably want updates to a User's Crime made in one place in your program to be visible to other parts of the program. This suggests that your Users should be managed by some sort of user controller which would be a reference type. e.g

    struct User {}
    class UserController {
        var users: [User]
    
        func add(user: User) { ... }
        func remove(userNamed: String) { ... }
        func ...
    }
    

    Collections are an interesting case. These include things like arrays and dictionaries, as well as strings. Are they copyable? Obviously. Is copying something you want to happen easily and often? That's less clear.

    Most languages say "no" to this and make their collections reference types. This is true in Objective-C and Java and Python and JavaScript and almost every other language I can think of. (One major exception is C++ with STL collection types, but C++ is the raving lunatic of the language world which does everything strangely.)

    Swift said "yes," which means that types like Array and Dictionary and String are structs rather than classes. They get copied on assignment, and on passing them as parameters. This is an entirely sensible choice as long as the copy is cheap, which Swift tries very hard to accomplish. ...

    I personally don't name my classes like that. I usually name mine UserManager instead of UserController but the idea is the same

    In addition don't use class when you have to override each and every instance of a function ie them not having any shared functionality.

    So instead of having several subclasses of a class. Use several structs that conform to a protocol.


    Another reasonable case for structs is when you want to do a delta/diff of your old and new model. With references types you can't do that out of the box. With value types the mutations are not shared.

    0 讨论(0)
  • 2020-11-22 06:03

    One point not getting attention in these answers is that a variable holding a class vs a struct can be a let while still allowing changes on the object's properties, while you cannot do this with a struct.

    This is useful if you don't want the variable to ever point to another object, but still need to modify the object, i.e. in the case of having many instance variables that you wish to update one after another. If it is a struct, you must allow the variable to be reset to another object altogether using var in order to do this, since a constant value type in Swift properly allows zero mutation, while reference types (classes) don't behave this way.

    0 讨论(0)
  • 2020-11-22 06:04

    As struct are value types and you can create the memory very easily which stores into stack.Struct can be easily accessible and after the scope of the work it's easily deallocated from the stack memory through pop from the top of the stack. On the other hand class is a reference type which stores in heap and changes made in one class object will impact to other object as they are tightly coupled and reference type.All members of a structure are public whereas all the members of a class are private.

    The disadvantages of struct is that it can't be inherited .

    0 讨论(0)
  • 2020-11-22 06:06

    Answering the question from the perspective of value types vs reference types, from this Apple blog post it would appear very simple:

    Use a value type [e.g. struct, enum] when:

    • Comparing instance data with == makes sense
    • You want copies to have independent state
    • The data will be used in code across multiple threads

    Use a reference type [e.g. class] when:

    • Comparing instance identity with === makes sense
    • You want to create shared, mutable state

    As mentioned in that article, a class with no writeable properties will behave identically with a struct, with (I will add) one caveat: structs are best for thread-safe models -- an increasingly imminent requirement in modern app architecture.

    0 讨论(0)
  • 2020-11-22 06:09

    Similarities between structs and classes.

    I created gist for this with simple examples. https://github.com/objc-swift/swift-classes-vs-structures

    And differences

    1. Inheritance.

    structures can't inherit in swift. If you want

    class Vehicle{
    }
    
    class Car : Vehicle{
    }
    

    Go for an class.

    2. Pass By

    Swift structures pass by value and class instances pass by reference.

    Contextual Differences

    Struct constant and variables

    Example (Used at WWDC 2014)

    struct Point{
     
       var x = 0.0;
       var y = 0.0;
    
    } 
    

    Defines a struct called Point.

    var point = Point(x:0.0,y:2.0)
    

    Now if I try to change the x. Its a valid expression.

    point.x = 5
    

    But if I defined a point as constant.

    let point = Point(x:0.0,y:2.0)
    point.x = 5 //This will give compile time error.
    

    In this case entire point is immutable constant.

    If I used a class Point instead this is a valid expression. Because in a class immutable constant is the reference to the class itself not its instance variables (Unless those variables defined as constants)

    0 讨论(0)
提交回复
热议问题