Conflicting definition of Swift struct and array

后端 未结 6 1102
孤街浪徒
孤街浪徒 2021-02-01 09:56

In Swift Programming Language, it says:

  1. “all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictio

6条回答
  •  無奈伤痛
    2021-02-01 10:55

    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.


    1st case

    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.


    2nd case

    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.


    3rd case

    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.


    5th case

    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.


    6th case

    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.


    not conclusion but observation instead

    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.

提交回复
热议问题