Swift 2.0 'inout' parameters and calculated properties

I am testing Swift 2.0 beta right now and have discovered strange behavior. Here is a sample code:

private func someFunc(inout someString: String) { print("Inside \'someFunc()\'") print(someString) someString = "Some another string" } private var someAncillaryInt = 42 print(someAncillaryInt) private var someString: String { get { print("Inside \'getter\'") return "Some string" } set { print("Inside \'setter\'") someAncillaryInt = 24 } } someFunc(&someString) print(someAncillaryInt) 

Output:

42

Inside the 'getter'

Inside 'someFunc ()'

Some line

Inside the 'setter'

24

I do not understand why the getter was not called inside someFunc() while printing someString , and why it was when someFunc() passed with someString .

It can be assumed that I still do not understand the complexities of inout parameters even after they are passed, because the inout parameter of the computed property ceases to be, em, is “computed”, but why was “setter” called when we set a different value to someString ?

Thanks!

UPD . I have added the answer below.

UPDATE 11/18/2015 . Apple has updated the guide with a detailed explanation of how inout params work.

+7
swift swift2
source share
3 answers

Your confusion may be caused by choosing someString as the name of a global variable, as well as the name of the someFunc() parameter.

print(someString) inside someFunc() prints the value of the (local) parameter of the function, which is completely someString (and hides) the global variable someString .

It’s easier to understand if you rename a function parameter

 private func someFunc(inout localString: String) { print("Inside \'someFunc()\'") print(localString) localString = "Some another string" } 

which is semantically identical (and therefore gives the same result).

You can think of

 someFunc(&someString) 

in the following way:

  • The value of someString (using the getter method).
  • someFunc() is executed with the localString parameter set to someString .
  • When returning from someFunc() , someString (using the setter method) is set to the (possibly modified) value of the localString parameter.

More information can be found at https://devforums.apple.com/thread/230567 from the Apple Developer Forum, for example:

Given the guarantee of getter and setter, inout follows naturally: when calling a function with the argument inout, it logically calls getter on var / subscript and copies the value to the temporary stack, which is guaranteed to have physical addressability. the physical address of the temporary is passed to the inout function argument .... A call no matter what it wants with this memory cell (and never knows everything that passed, was calculated or not). When the called is returned, setter is called to copy the value into place.

and

It also ensures that the getter / setter of the property passed inout will have its receiver and setter once, regardless of whether it is callle (this is important if the accessories have side effects or are expensive). A.

but it is also indicated that, if necessary, a temporary copy is excluded.

+6
source share

I don’t understand why getter was not called when printing someString inside someFunc () and why it was when someFunc () received passed with someString.

getter not called while printing someString inside someFunc() , because it has already been called. We already have this line as the parameter someString , internal to someFunc() ; we do not need to receive it again.

Your conclusion reads:

 Inside 'getter' //<-- that the getter being called! Inside 'someFunc()' Some string Inside 'setter' 

Your code works:

 someFunc(&someString) //<-- that calls the getter! 

This has nothing to do, by the way, with inout . You will see the same thing (as for getter ) if it was a normal parameter.

+1
source share

@ Martin R pointed me to the aforementioned thread and, in particular, comment No. 16 made by Chris Lattner (Swift mastermind), which helped me understand “ignorance”. Thanks man!

Consider this code:

 private var someString: String { get { print("Inside getter") return "Some string" } set { print("Inside setter") } } private func someFunc(inout stringArg: String) { let funcName = "`someFunc()\'" print("Inside " + funcName) let someDontMatter0 = 42, someDontMatter1 = 24 print(stringArg) // sets temporary, not the original one. Hence, no calls to setter stringArg = "Some other string" stringArg = "No matter what string" print("These \(someDontMatter0) and \(someDontMatter1)") print("Leaving " + funcName) // when callee returns, calls the setter with "No matter what // string" as a `newValue' } // implicitly creates temporary initialised with `someString' value // getting from `someString getter. someFunc(&someString) 

For someone from a C ++ background, it might seem that the output should look something like this:

Inside `someFunc () '

Inside the recipient

Some line

Internal setter

These 42 and 24

Exit from `someFunc () '

If the actual conclusion is as follows:

Inside the recipient

Inside `someFunc () '

Some line

These 42 and 24

Exit from `someFunc () '

Internal setter

Cool, right?

This anti-intuitive behavior for someone with a C ++ background is the result of a more fundamental logic that underlies many aspects of Swift. As you can see from Chris’s comment, the general Swift compiler way associated with inout properties is to create a temporary object on the stack to keep the original value there (hence calling getter).

So, when you manipulate your inout parameter, you are dealing with a temporary object (with the initial value that you passed to the function), but not with the original one (for me personally this is a bit vague. But well, you can handle it). And finally:

When the called party returns, the caller is activated to copy the value to the place

So, if you change the code a bit:

 private var someString: String { get { print("Inside getter") return "Some string" } set { print("Inside setter") } } private func someFunc(inout stringArg: String) { // before returning calls `someString setter } // calls `someString getter someFunc(&someString) 

the output will be:

Inside the recipient

Internal setter

The same applies to saved properties:

 private func someFunc() { var someString = "Some string" { willSet { print("Inside \'willSet\' with \'newValue\': \(newValue)") } didSet { print("Inside \'didSet\' with \'oldValue\': \(oldValue)") } } func someOtherFunc(inout stringArg: String) { print("Inside `someOtherFunc()\'") stringArg = "First string" stringArg = "Second string" print("Before leaving the function") } someOtherFunc(&someString) } someFunc() 

Output:

Inside `someOtherFunc () '

Before leaving a function

Inside 'willSet' with 'newValue': second line

Inside 'didSet' with 'oldValue': some string

From Chris's answer:

It also ensures that the getter / setter of the property passed inout will have its getter and setter once no matter what the caller does (this is important if the accessories have side effects or roads)

OK, but what if some method that in some cases accepts the inout parameter does not change it (does not call setter or does not change this temporary variable, you name it)? Also, what if I conceived some important logic of my computed variable getter and setter and came across these specific circumstances (just imagine that I open and close files inside getter / setter)? Getters and setters will be called when they are better off.

I would really like to see Chris answer in one form or another in Apple's final Swift 2.0 programming guide.

0
source share

All Articles