Does protocol array elements passed by value or reference?

335 Views Asked by At

I know Structs are passed by value and classes are passed by reference in Swift.

I wonder if I create an array store elements which providing a protocol. These elements passed by value or reference?

Is it based on the definition of model a class or struct?

class ClassA: ProtocolA {
// something
}

struct StructA: ProtocolA {
// something
}

var arr: [ProtocolA] = [ClassA(), StructA()]

exampleFunction(arr[0]) // is it passed by reference
exampleFunction(arr[1]) // is it passed by value

3

There are 3 best solutions below

5
On BEST ANSWER

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
}
0
On

When you store a variable as a protocol type, the compiler handles it as if it was a value type. So if exampleFunction takes an input argument of type ProtocolA, in order for you to be able to mutate a property of the input argument, you need to declare it as inout.

This doesn't mean that the values are necessarily passed by value, it just means the compiler has no idea whether the input argument is a value or a reference type, so at compile-time it handles it as a value type.

If you want to be able to handle a protocol variable as a reference type, you need to make the protocol class bound. protocol ProtocolA: class {}

3
On

Regardless of how the array is declared, the class is passed by reference, and the structure is passed by value. This can be demonstrated with the following example:

protocol ProtocolA {
    var title: String { get set}
}

class ClassA: ProtocolA {
    var title = "ClassA"
}

struct StructA: ProtocolA {
    var title = "StructA"
}

var arr: [ProtocolA] = [ClassA(), StructA()]

print(arr[0].title) // ClassA
print(arr[1].title) // StructA

func exampleFunction(_ obj: ProtocolA) {
    var obj = obj // Create local mutable variable
    obj.title = obj.title + "!!!"
}

exampleFunction(arr[0])
exampleFunction(arr[1])

print(arr[0].title) // ClassA!!!
print(arr[1].title) // StructA