Why Choose Struct Over Class?

前端 未结 16 2092

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:10

    Structs are value type and Classes are reference type

    • Value types are faster than Reference types
    • Value type instances are safe in a multi-threaded environment as multiple threads can mutate the instance without having to worry about the race conditions or deadlocks
    • Value type has no references unlike reference type; therefore there is no memory leaks.

    Use a value type when:

    • You want copies to have independent state, the data will be used in code across multiple threads

    Use a reference type when:

    • You want to create shared, mutable state.

    Further information could be also found in the Apple documentation

    https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html


    Additional Information

    Swift value types are kept in the stack. In a process, each thread has its own stack space, so no other thread will be able to access your value type directly. Hence no race conditions, locks, deadlocks or any related thread synchronization complexity.

    Value types do not need dynamic memory allocation or reference counting, both of which are expensive operations. At the same time methods on value types are dispatched statically. These create a huge advantage in favor of value types in terms of performance.

    As a reminder here is a list of Swift

    Value types:

    • Struct
    • Enum
    • Tuple
    • Primitives (Int, Double, Bool etc.)
    • Collections (Array, String, Dictionary, Set)

    Reference types:

    • Class
    • Anything coming from NSObject
    • Function
    • Closure
    0 讨论(0)
  • 2020-11-22 06:12

    Here are some other reasons to consider:

    1. structs get an automatic initializer that you don't have to maintain in code at all.

      struct MorphProperty {
         var type : MorphPropertyValueType
         var key : String
         var value : AnyObject
      
         enum MorphPropertyValueType {
             case String, Int, Double
         }
       }
      
       var m = MorphProperty(type: .Int, key: "what", value: "blah")
      

    To get this in a class, you would have to add the initializer, and maintain the intializer...

    1. Basic collection types like Array are structs. The more you use them in your own code, the more you will get used to passing by value as opposed to reference. For instance:

      func removeLast(var array:[String]) {
         array.removeLast()
         println(array) // [one, two]
      }
      
      var someArray = ["one", "two", "three"]
      removeLast(someArray)
      println(someArray) // [one, two, three]
      
    2. Apparently immutability vs. mutability is a huge topic, but a lot of smart folks think immutability -- structs in this case -- is preferable. Mutable vs immutable objects

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

    I wouldn't say that structs offer less functionality.

    Sure, self is immutable except in a mutating function, but that's about it.

    Inheritance works fine as long as you stick to the good old idea that every class should be either abstract or final.

    Implement abstract classes as protocols and final classes as structs.

    The nice thing about structs is that you can make your fields mutable without creating shared mutable state because copy on write takes care of that :)

    That's why the properties / fields in the following example are all mutable, which I would not do in Java or C# or swift classes.

    Example inheritance structure with a bit of dirty and straightforward usage at the bottom in the function named "example":

    protocol EventVisitor
    {
        func visit(event: TimeEvent)
        func visit(event: StatusEvent)
    }
    
    protocol Event
    {
        var ts: Int64 { get set }
    
        func accept(visitor: EventVisitor)
    }
    
    struct TimeEvent : Event
    {
        var ts: Int64
        var time: Int64
    
        func accept(visitor: EventVisitor)
        {
            visitor.visit(self)
        }
    }
    
    protocol StatusEventVisitor
    {
        func visit(event: StatusLostStatusEvent)
        func visit(event: StatusChangedStatusEvent)
    }
    
    protocol StatusEvent : Event
    {
        var deviceId: Int64 { get set }
    
        func accept(visitor: StatusEventVisitor)
    }
    
    struct StatusLostStatusEvent : StatusEvent
    {
        var ts: Int64
        var deviceId: Int64
        var reason: String
    
        func accept(visitor: EventVisitor)
        {
            visitor.visit(self)
        }
    
        func accept(visitor: StatusEventVisitor)
        {
            visitor.visit(self)
        }
    }
    
    struct StatusChangedStatusEvent : StatusEvent
    {
        var ts: Int64
        var deviceId: Int64
        var newStatus: UInt32
        var oldStatus: UInt32
    
        func accept(visitor: EventVisitor)
        {
            visitor.visit(self)
        }
    
        func accept(visitor: StatusEventVisitor)
        {
            visitor.visit(self)
        }
    }
    
    func readEvent(fd: Int) -> Event
    {
        return TimeEvent(ts: 123, time: 56789)
    }
    
    func example()
    {
        class Visitor : EventVisitor
        {
            var status: UInt32 = 3;
    
            func visit(event: TimeEvent)
            {
                print("A time event: \(event)")
            }
    
            func visit(event: StatusEvent)
            {
                print("A status event: \(event)")
    
                if let change = event as? StatusChangedStatusEvent
                {
                    status = change.newStatus
                }
            }
        }
    
        let visitor = Visitor()
    
        readEvent(1).accept(visitor)
    
        print("status: \(visitor.status)")
    }
    
    0 讨论(0)
  • 2020-11-22 06:15

    According to the very popular WWDC 2015 talk Protocol Oriented Programming in Swift (video, transcript), Swift provides a number of features that make structs better than classes in many circumstances.

    Structs are preferable if they are relatively small and copiable because copying is way safer than having multiple references to the same instance as happens with classes. This is especially important when passing around a variable to many classes and/or in a multithreaded environment. If you can always send a copy of your variable to other places, you never have to worry about that other place changing the value of your variable underneath you.

    With Structs, there is much less need to worry about memory leaks or multiple threads racing to access/modify a single instance of a variable. (For the more technically minded, the exception to that is when capturing a struct inside a closure because then it is actually capturing a reference to the instance unless you explicitly mark it to be copied).

    Classes can also become bloated because a class can only inherit from a single superclass. That encourages us to create huge superclasses that encompass many different abilities that are only loosely related. Using protocols, especially with protocol extensions where you can provide implementations to protocols, allows you to eliminate the need for classes to achieve this sort of behavior.

    The talk lays out these scenarios where classes are preferred:

    • Copying or comparing instances doesn't make sense (e.g., Window)
    • Instance lifetime is tied to external effects (e.g., TemporaryFile)
    • Instances are just "sinks"--write-only conduits to external state (e.g.CGContext)

    It implies that structs should be the default and classes should be a fallback.

    On the other hand, The Swift Programming Language documentation is somewhat contradictory:

    Structure instances are always passed by value, and class instances are always passed by reference. This means that they are suited to different kinds of tasks. As you consider the data constructs and functionality that you need for a project, decide whether each data construct should be defined as a class or as a structure.

    As a general guideline, consider creating a structure when one or more of these conditions apply:

    • The structure’s primary purpose is to encapsulate a few relatively simple data values.
    • It is reasonable to expect that the encapsulated values will be copied rather than referenced when you assign or pass around an instance of that structure.
    • Any properties stored by the structure are themselves value types, which would also be expected to be copied rather than referenced.
    • The structure does not need to inherit properties or behavior from another existing type.

    Examples of good candidates for structures include:

    • The size of a geometric shape, perhaps encapsulating a width property and a height property, both of type Double.
    • A way to refer to ranges within a series, perhaps encapsulating a start property and a length property, both of type Int.
    • A point in a 3D coordinate system, perhaps encapsulating x, y and z properties, each of type Double.

    In all other cases, define a class, and create instances of that class to be managed and passed by reference. In practice, this means that most custom data constructs should be classes, not structures.

    Here it is claiming that we should default to using classes and use structures only in specific circumstances. Ultimately, you need to understand the real world implication of value types vs. reference types and then you can make an informed decision about when to use structs or classes. Also, keep in mind that these concepts are always evolving and The Swift Programming Language documentation was written before the Protocol Oriented Programming talk was given.

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

    Structure is much more faster than Class. Also, if you need inheritance then you must use Class. Most important point is that Class is reference type whereas Structure is value type. for example,

    class Flight {
        var id:Int?
        var description:String?
        var destination:String?
        var airlines:String?
        init(){
            id = 100
            description = "first ever flight of Virgin Airlines"
            destination = "london"
            airlines = "Virgin Airlines"
        } 
    }
    
    struct Flight2 {
        var id:Int
        var description:String
        var destination:String
        var airlines:String  
    }
    

    now lets create instance of both.

    var flightA = Flight()
    
    var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )
    

    now lets pass these instance to two functions which modify the id, description, destination etc..

    func modifyFlight(flight:Flight) -> Void {
        flight.id = 200
        flight.description = "second flight of Virgin Airlines"
        flight.destination = "new york"
        flight.airlines = "Virgin Airlines"
    }
    

    also,

    func modifyFlight2(flight2: Flight2) -> Void {
        var passedFlight = flight2
        passedFlight.id = 200
        passedFlight.description = "second flight from virgin airlines" 
    }
    

    so,

    modifyFlight(flight: flightA)
    modifyFlight2(flight2: flightB)
    

    now if we print the flightA's id and description, we get

    id = 200
    description = "second flight of Virgin Airlines"
    

    Here, we can see the id and description of FlightA is changed because the parameter passed to the modify method actually points to the memory address of flightA object(reference type).

    now if we print the id and description of FLightB instance we get,

    id = 100
    description = "first ever flight of Virgin Airlines"
    

    Here we can see that the FlightB instance is not changed because in modifyFlight2 method, actual instance of Flight2 is passes rather than reference ( value type).

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

    With classes you get inheritance and are passed by reference, structs do not have inheritance and are passed by value.

    There are great WWDC sessions on Swift, this specific question is answered in close detail in one of them. Make sure you watch those, as it will get you up to speed much more quickly then the Language guide or the iBook.

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