In Swift Programming Language, it says:
“all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictio
bases on interferece between #2 and #3, I'm trying to find the answer to your question via an example in practice. it is truly based on Swift types only.
I have an Array
here, and I fill it up with random numbers.
var arrayOfNumbers: Array = Array()
for _ in 0..100 {
arrayOfNumbers.append(arc4random())
}
I will check what it returns:
let myReturnedArray: Array = self.myReferenceTest(arrayOfNumbers, referenceArray: &arrayOfNumbers)
if arrayOfNumbers === myReturnedArray {
println("(rS)") // returned value is the same instance
} else {
println("(rD)") // returned value is a different instance
}
and I have a test method with two parameters, the first is the Array
itself, the second one is just the reference of the same Array
. I'm trying to do different things in that method to see what will happen.
func myReferenceTest (directory: Array Array {
if directArray === referenceArray {
println("(pS)") // the same instance...
} else {
println("(pD)") // different instance ...
}
return directArray
}
that will print "(pS)"
, so it looks the reference has been passed over as first paramater not the copy of the struct. the consol says "(rS)"
, the the returned value was the same reference and not a copy.
func myReferenceTest(directArray: Array, inout referenceArray: Array) -> Array {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
directArray[0] = 12
return directArray
}
it says still the same "pS"
and "rS"
, but if I print the original array's [0]
element it is updated to 12
, however directArray
was not an inout
paremeter at all. the reference was passed over, and the reference was returned, and I also called a non-mutating method on the array, and I was able to make certain changes.
func myReferenceTest(directArray: Array, inout referenceArray: Array) -> Array {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
var myOtherArray = directArray
myOtherArray.append(arc4random())
return myOtherArray
}
the console says "pS"
but "rD"
becase I've called a mutating
method in the directArray
that caused the array was copied (O(n)
), and I made the changes on another instance.
func myReferenceTest(directArray: Array, inout referenceArray: Array) -> Array {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
var myOtherArray = directArray
myOtherArray.append(arc4random())
referenceArray = myOtherArray
return myOtherArray
}
same as the recent one, but it will say on console "pS"
and "rS"
again. so it seems the reference was returned and not the copy of the myOtherArray
.
func myReferenceTest(directArray: Array, inout referenceArray: Array) -> Array {
if directArray === referenceArray {
println("(pS)")
} else {
println("(pD)")
}
referenceArray.append(arc4random())
directArray[0] = 12
return directArray
}
it will show "pS"
and "rD"
again, and the reference array's first element is 12
and it looks the directArray
was copied after I've call a mutating
method on the referenceArray
. you can check it without doubt: the referenceArray
is still identical to my original array but the directArray
is different now.
If I don't return the directArray
that will be released at all when the method is run out of its scope, becase the copy is created in the method's scope only.
it seems the Array
is always creates a new instance of itself when you call a mutating
method on it, which is in accordance with the Swift documantation about the mutating
keyword:
However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit
self
property, and this new instance will replace the existing one when the method ends.
(source)
that behaviour is pretty much as same as the Swift logic here: the compiler tries to not extend the code with copying of any object until it certainly necessary, which looks – in the case of Array
at least – happaning when a mutating
method is called on the object.
you will find more information about which methods of which Swift types are mutating
in the Swift Standard Library Reference, here.
nevertheless, even if the Array
looks to be defined as a struct, it defintely behaves like a class, because they have some class-level privileges:
- Type casting enables you to check and interpret the type of a class instance at runtime.
- Reference counting allows more than one reference to a class instance.
I have not found evidence the rest two privileges of classes, but having these two class-level privilages indicates the Array
is definitely more than a simple struct
, which makes the statement #1 ambiguous.
having those prvileges indicates the reference is being passed (logically) in every case.
the Dictionary
is a simpliest case, becase it looks an plain NSMutableDictionary
behind the scenes, when I ask the class's name via object_getClassName(...)
, it clearly refers to an instance of a mutable dictionary from Obj-C, which is defintely not a struct
even if the declaration of Dictionary
indicates a struct
.
it is pontless to go further in guessing of what happens in the engine, the compiler is still Beta so we don't know how the final compiler will work; evenetually many conspiration-theory can be defined about them (or already hve been), I've tried to focus on the facts only which can be provable during identifying them in background, which may be changed in the future, but those are the facts currently.