What does an ampersand (&) mean in the Swift language?

前端 未结 5 461
没有蜡笔的小新
没有蜡笔的小新 2020-12-01 02:29

I know about the ampersand as a bit operation but sometimes I see it in front of variable names. What does putting an & in front of variables do?

5条回答
  •  感情败类
    2020-12-01 03:13

    As noted in other answers, you use prefix & to pass a value to an inout parameter of a method or function call, as documented under Functions > Function Argument Labels and Parameter Names > In-Out Parameters in The Swift Programming Language. But there's more to it than that.

    You can, in practice, think about Swift inout parameters and passing values to them as being similar to C or C++ pass-by-address or pass-by-reference. In fact, the compiler will optimize many uses of inout parameters down to roughly the same mechanics (especially when you're calling imported C or ObjC APIs that deal in pointers). However, those are just optimizations — at a semantic level, inout really doesn't pass addresses around, which frees the compiler to make this language construct more flexible and powerful.

    For example, here's a struct that uses a common strategy for validating access to one of its properties:

    struct Point {
        private var _x: Int
        var x: Int {
            get {
                print("get x: \(_x)")
                return _x
            }
            set {
                print("set x: \(newValue)")
                _x = newValue
            }
        }
        // ... same for y ...
        init(x: Int, y: Int) { self._x = x; self._y = y }
    }
    

    (In "real" code, the getter and setter for x could do things like enforcing minimum/maximum values. Or x could do other computed-property tricks, like talking to a SQL database under the hood. Here we just instrument the call and get/set the underlying private property.)

    Now, what happens when we pass x to an inout parameter?

    func plusOne(num: inout Int) {
        num += 1
    }
    
    var pt = Point(x: 0, y: 1)
    plusOne(num: &pt.x)
    // prints:
    //   get x: 0
    //   set x: 1
    

    So, even though x is a computed property, passing it "by reference" using an inout parameter works the same as you'd expect it to if x were a stored property or a local variable.


    This means that you can pass all sorts of things "by reference" that you couldn't even consider in C/C++/ObjC. For example, consider the standard library swap function, that takes any two... "things" and switches their values:

    var a = 1, b = 2
    swap(&a, &b)
    print(a, b) // -> 2 1
    
    var dict = [ "Malcolm": "Captain", "Kaylee": "Mechanic" ]
    swap(&dict["Malcolm"], &dict["Kaylee"])
    print(dict) // -> ["Kaylee": "Captain", "Malcolm": "Mechanic"], fanfic ahoy
    
    let window1 = NSWindow()
    let window2 = NSWindow()
    window1.title = "window 1"
    window2.title = "window 2"
    var windows = [window1, window2]
    swap(&windows[0], &windows[1])
    print(windows.map { $0.title }) // -> ["window 2", "window 1"]
    

    The the way inout works also lets you do fun stuff like using the += operator on nested call chains:

    window.frame.origin.x += 10
    

    ... which is a whole lot simpler than decomposing a CGRect just to construct a new one with a different x coordinate.


    This more nuanced version of the inout behavior, called "call by value result", and the ways it can optimize down to C-style "pass by address" behavior, is covered under Declarations > Functions > In-Out Parameters in The Swift Programming Language.

提交回复
热议问题