Swift: Protocol VS. Struct VS. Class

Swift: Protocol vs. Struct vs. Class

These analogies are not "exactly" correct, but this is the gist of it as I understand it

  1. Yes, Protocols are effectively like interfaces

  2. Classes are classes, like in Java/Android, and pretty much any other language

  3. Structs are like classes, but they are passed by-value (copied) when passing them from one variable/function to another.
    If you're familiar with C# at all, it's implementation of structs is very similar.

Eg:

class Foo {
...
}

let x = Foo()
let z = x

At this point x and z both refer to the same Object in memory, there is only one Foo object

struct Bar {
...
}

let a = Bar()
let b = a

When assigning b, a is copied (think of basically copying the memory block). At this point, there are two independent Bar objects in memory and modifying one doesn't affect the other.

Why is this useful? Sometimes you don't want a shared reference, but mostly for performance reasons. Because structs don't have to all refer to the same object, they don't have to be allocated on the heap. They can instead often be allocated on the stack, which is much faster. Also arrays of structs can be implemented as one big contiguous block of memory which means it's much friendlier on the CPU cache if you want to iterate through it all.

Swift isn't garbage collected, but for garbage collected languages like C# this can mean the garbage collector doesn't have to deal with a lot of objects that it might otherwise have to. Even in swift the struct copying means it can avoid doing the Retain/Release behind the scenes neccessary for ARC, which can help a lot.

The primary use case for structs is when you have lots of immutable "simple data" like a Vector (set of 3 floating point values)

Why protocol is better than class in swift?

Lets take a downloading example.

You have a Base class FileDownloadModel, and have 3 subclasses AudioFileDownloadModel, VideoFileDownloadModel, and ImageDownloadModel.

You have a DownloadManager that takes a FileDownloadModel input and uses this model's urlToDownload property to download the file.

Later down the line you are told that there is one more model coming but it's of type UserDownloadModel which is a subclass of User, and not FileDownloadModel.

See now it becomes difficult to handle this scenario where you will have to change a lot of code to incorporate downloading methods.

How protocol oriented programming will help you here:

  1. Create a protocol named DownloadingFileProtocol and add
    methods and properties that you need for downloading a file. eg. urlToDownload,
    pathToSave, extension etc.
  2. Implement the same protocol in FileDownloadModel and
    UserDownloadModel. The benefit here is that you don't have to change a
    lot of code in UserDownloadModel. You will just implement the
    methods from the DownloadingFileProtocol.
  3. If a new entity comes down the line again, you will not
    change any code. Rather, you'll just implement the protocol methods.
  4. And now your DownloadManager can take a
    DownloadingFileProtocol as input instead of a specific model. As well, you can now make any model "downloadable" by having it adopt this protocol.

Identifiable protocol in Swift: class vs struct

As the documentation of Identifiable states, it does provide a default implementation for id for class types. However, there is no default implementation for structs, hence you need to add the property manually.

A class name can be the same as a protocol name in Swift?

A class name can only be the same as a protocol name if they are in different modules, or if the class is nested inside another type. Even then, it will probably be confusing.

When to switch from struct to class in swift?

is there a way to provide somekind of default implementations for functions in protocols, so i don't need to write them every time again, when confirming to a protocol?

There is indeed a way to provide default method implementations for protocol-conforming types:

protocol MyProtocol {
func myMethodRequirement()
}

extension MyProtocol {
func myMethodRequirement() {
// Default implementation.
}
}

struct Foo: MyProtocol {
func myMethodRequirement() {
// Foo-specific implementation.
}
}

struct Bar: MyProtocol {
/* Inherits default implementation instead. */
}

In your case, giving sendSomeMessage(message:) a default implementation would look like:

extension Game {
func sendSomeMessage(message: String) {
// Default implementation here.
}
}

Any type conforming to Game could either implement sendSomeMessage(message:) itself (similar to override-ing a method in a class), or use the default implementation without doing any additional work.

For more information, you can see the "Providing Default Implementations" section on protocols of the The Swift Programming Language guide.


To answer the title of your question, then, given that it is possible to do this for structs:

  • A class may be useful over a struct if you have an inheritance hierarchy multiple levels deep, which may be more complicated to express using protocol conformances
  • A class may be useful over a struct when you need reference semantics — see:
    • Structures and Enumerations are Value Types and
    • Classes are Reference Types

In your specific case, it doesn't immediately appear that either of these apply, but with more information, it might become obvious whether switching would be beneficial or not.

What is difference between '&' and ',' in swift about Protocol Composition

From the Swift documentation on protocols, I think & is mainly used when you are requiring a variable to adopt to multiple protocols, ex:

func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}

And you use , when you want to adopt a custom struct/class to multiple protocols:

struct SomeStructure: FirstProtocol, AnotherProtocol {
// structure definition goes here
}

How should I reason when I have to choose between a class, struct and enum in Swift?

ChristopheD's and Jack Wu's answers are good, but I feel they don't touch on enums, or miss their importance. Swift enums are (meant to be) a full implementation of algebraic data types. Classes and structs are traditionally used to model data in object-oriented languages, but enums are usually limited to being used as a convenient way to limit the value of a variable to a limited number of possibilities. E.g. (C++):

enum MaritalStatus { Unmarried, Married, Divorced, WidowedOrWidowered };
MaritalStatus m = Unmarried;

Swift enums can do the above but they can do a lot more. Of course the Language Guide has a pretty good barcode modelling example but the best example I know of that really drives home the point of modelling data with algebraic data types is Scott Wlaschin's presentation: http://www.slideshare.net/ScottWlaschin/ddd-with-fsharptypesystemlondonndc2013

You would probably benefit from going through the whole presentation but really to 'get' the point all you need to see is slide 60 where he shows how to model a 'payment method' in a typical line-of-business app.

The examples in the presentation are in F# but F# isn't that far off from Swift and you can pretty easily map between them. E.g., the payment methods enum in Swift would look like:

enum PaymentMethod {
case cash // No extra data needed.
case cheque(Int) // Cheque #.
case card(CardType, CardNumber) // 2 pieces of extra data.
}

The point of the above is that each order's payment method can be only one of the above three methods. Anything else will not be allowed by the compiler. This is a very succinct alternative to building entire class hierarchies to model these almost trivial things.

The presentation really takes off from there and the best part is Swift can do almost everything that F# can in terms of data modelling, using optional types, etc.

Does protocol array elements passed by value or reference?

Protocols should be seen as value types because you need to explicitly tell the compiler that it is a reference type by defining it as conforming to AnyObject (which all classes conform to)

So if you have

protocol ProtocolB: AnyObject

then any type conforming to ProtocolB will be sent by reference and otherwise not.

Here is a simplified example

protocol ProtocolA {
var x: Int { get set }
}

protocol ProtocolB: AnyObject {
var y: Int { get set }
}

class ClassA: ProtocolA, ProtocolB {
var x = 0
var y = 0
}

func exampleFunction(_ object: ProtocolA) {
object.x += 2 // <-- This will generate a compilation error
}

func exampleFunction(_ object: ProtocolB) {
object.y += 2 // This is fine
}

Class does not conform to protocol, but struct does

It does not compile because your

extension MyProtocol where Self == MyClass

provides a default method only for MyClass itself, but not for possible subclasses. Changing the constraint to

extension MyProtocol where Self: MyClass

makes the code compile. Alternatively prevent the creation of subclasses with

final class MyClass : MyProtocol {}

This is not a problem for MyStruct because struct types cannot be inherited from in Swift.

When to use `protocol` and `protocol: class` in Swift?

Swift 4 version

AnyObject added to a protocol definition like this

protocol FilterViewControllerDelegate: AnyObject  {
func didSearch(parameters:[String: String]?)
}

means that only a class will be able to conform to that protocol.

So given this

protocol FilterViewControllerDelegate: AnyObject  {
func didSearch(parameters:[String: String]?)
}

You will be able to write this

class Foo: FilterViewControllerDelegate {
func didSearch(parameters:[String: String]?) { }
}

but NOT this

struct Foo: FilterViewControllerDelegate {
func didSearch(parameters:[String: String]?) { }
}

Swift 3 version

:class added to a protocol definition like this

protocol FilterViewControllerDelegate: class  {
func didSearch(Parameters:[String: String]?)
}

means that only a class will be able to conform to that protocol.

So given this

protocol FilterViewControllerDelegate: class  {
func didSearch(Parameters:[String: String]?)
}

You will be able to write this

class Foo: FilterViewControllerDelegate {
func didSearch(Parameters:[String: String]?) { }
}

but NOT this

struct Foo: FilterViewControllerDelegate {
func didSearch(Parameters:[String: String]?) { }
}


Related Topics



Leave a reply



Submit