Is swift inout parameter a variable or a pointer?
Asked Answered
F

2

6

I feel a bit lost using swift inout parameter in the following code:

var shouldContinue: Bool = true

func doSomeWork1(shouldContinue: inout Bool)
{
    while shouldContinue
    {
        // ERROR: the compiler wants: doSomeWork2(shouldContinue: &shouldContinue)
        doSomeWork2(shouldContinue: shouldContinue)
    }
}

func doSomeWork2(shouldContinue: inout Bool)
{
    while shouldContinue
    {

    }
}

Why does the compiler want doSomeWork2(shouldContinue: &shouldContinue) instead of the compiler wants: doSomeWork2(shouldContinue: shouldContinue) ? isn't shouldContinue already a pointer in the scope of doSomeWork1() ???

Fylfot answered 12/11, 2016 at 21:15 Comment(0)
L
11

Being a pointer is only side-effect of optimization process for inout parameters. They actually work in different manner using copy-in copy-out behavior. So inside function that parameter is treated just like regular variable, not pointer. If you pass it to another function that takes inout parameter you have to mark it as such.

In-out parameters are passed as follows:

When the function is called, the value of the argument is copied.

In the body of the function, the copy is modified.

When the function returns, the copy’s value is assigned to the original argument.

This behavior is known as copy-in copy-out or call by value result. For example, when a computed property or a property with observers is passed as an in-out parameter, its getter is called as part of the function call and its setter is called as part of the function return.

As an optimization, when the argument is a value stored at a physical address in memory, the same memory location is used both inside and outside the function body. The optimized behavior is known as call by reference; it satisfies all of the requirements of the copy-in copy-out model while removing the overhead of copying. Write your code using the model given by copy-in copy-out, without depending on the call-by-reference optimization, so that it behaves correctly with or without the optimization.

In-Out Parameters

Lith answered 12/11, 2016 at 21:30 Comment(7)
Thanks for your reply. If I understand you correctly, copy-in copy-out behavior means that I cannot use my bool variable to cancel the work done on an other thread ? Il mean if I change the value of the original shouldContinue it won't change inside the functions, but the old value will be restored when returning from the functions ?Fylfot
Yes, you cannot use it between threads. What you can do is to create class with boolean field and then use instance of that class in multiple threads. And you would not need inout parameter in that case. Class instance is reference (pointer) that will get passed around and you will be reading and writing same boolean value from multiple threads..Lith
is there a "simpler way"? Creating a class holding a boolean seems a bit overkill / messy.Fylfot
AFAIK no. The only simpler way would be having global boolean that you read directly.Lith
IMO using class instance as parameter is more flexible and better than using global boolean.Lith
Thanks for your help.Fylfot
@DalijaPrasnikar would the getter/setter of the property be called in case of optimisation ?Lek
G
1

From: Matt Neuburg Book “iOS 13 Programming Fundamentals with Swift.” :

If we want a function to alter the original value of an argument passed to it, we must do the following:

  • The type of the parameter we intend to modify must be declared inout.
  • When we call the function, the variable holding the value to be modified must be declared with var, not let.
  • Instead of passing the variable as an argument, we must pass its address. This is done by preceding its name with an ampersand (&).

Our removeCharacter(_:from:) now looks like this:

 func removeCharacter(_ c:Character, from s: inout String) -> Int {
  var howMany = 0
  while let ix = s.firstIndex(of:c) {
    s.remove(at:ix)
    howMany += 1
  }
 return howMany
}

And our call to removeCharacter(_:from:) now looks like this: var s = "hello" let result = removeCharacter("l", from:&s) After the call, result is 2 and s is "heo". Notice the ampersand before the name s when we pass it as the from: argument. It is required; if you omit it, the compiler will stop you. I like this requirement, because it forces us to acknowledge explicitly to the compiler, and to ourselves, that we’re about to do something potentially dangerous: we’re letting this function, as a side effect, modify a value outside of itself.

Gio answered 16/10, 2019 at 22:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.