Swift make method parameter mutable?
As stated in other answers, as of Swift 3 placing var before a variable has been deprecated. Though not stated in other answers is the ability to declare an inout
parameter. Think: passing in a pointer.
func reduceToZero(_ x: inout Int) {
while (x != 0) {
x = x-1
}
}
var a = 3
reduceToZero(&a)
print(a) // will print '0'
This can be particularly useful in recursion.
Apple's inout
declaration guidelines can be found here.
Making immutable function parameter mutable
You could either make that array available to all functions by declaring it as a property (and removing the class
modifier of your function) or you could define your function's parameter as an in-out parameter. By adding the inout
keyword right before the parameter's type, you can make the parameter mutable by keeping the reference to the passed array.
Nevertheless, I would recommend you to make your array into a property – simply because it's easier and, in my opinion, better maintainable.
Modifying an array passed as an argument to a function in Swift
You need to change the definition of modify
to accept an inout
parameter. By default, function arguments are immutable, but by using the inout
keyword, you can make them mutable. You also need to pass the argument by reference.
func modify( arr: inout [String]) {
arr.removeFirst()
}
var stringArr = ["a","b"]
modify(arr: &stringArr) //stringArr = ["b"] after the function call
Is there any way to make the method return a mutable value?
The callAsFunction
simply returns (a copy of the) Person
, which is a value type. You cannot then mutate the property of it like that. It is equivalent to the following:
struct Person {
var name: String
}
Person(name: "Foo").name = "Bar"
That returns the same error:
If Person
was a reference type, it would have worked, but not for a value type. And even if you took your value type, and first assigned it to a variable before mutating it, you would only be mutating your copy, not the original.
If you want the behavior you want, you would use a @dynamicMemberLookup
as suggested by matt (+1) and outlined in SE-0195.
You said:
I can't use
dynamicMemberLookup
because I don't know what method or property there will be inPerson
. For example, there may be 100 methods and properties inPerson
(not only one name property as demonstrated), and it is impossible for me to write 100 subscript methods withdynamicMemberLookup
.
You do not need “100 subscript methods.” It is the motivating idea behind @dynamicMemberLookup
, namely that the properties will be determined dynamically. E.g., here is Person
with two properties, but Group
only has the one @dynamicMemberLookup
.
struct Person {
var name: String
var city: String
}
@dynamicMemberLookup
struct Group {
var person: Person
subscript(dynamicMember keyPath: WritableKeyPath<Person, String>) -> String {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}
var group = Group(person: Person(name: "James", city: "New York"))
group.name = "Wong"
group.city = "Los Angeles"
print(group.person) // Person(name: "Wong", city: "Los Angeles")
If you want to handle different types, make it generic:
struct Person {
var name: String
var city: String
var age: Int
}
@dynamicMemberLookup
struct Group {
var person: Person
subscript<T>(dynamicMember keyPath: WritableKeyPath<Person, T>) -> T {
get { person[keyPath: keyPath] }
set { person[keyPath: keyPath] = newValue }
}
}
And
var group = Group(person: Person(name: "James", city: "New York", age: 41))
group.name = "Wong"
group.city = "Los Angeles"
group.age = 42
print(group.person) // Person(name: "Wong", city: "Los Angeles", age: 42)
Why are function parameters immutable in Swift?
The motivation for this is described here: https://github.com/apple/swift-evolution/blob/master/proposals/0003-remove-var-parameters.md
tl;dr: it avoids confusion with the inout
keyword.
Mutate function parameters in Swift
It seems from your example that you need to change the passed arguments inside the function as a side effect, and need the updated values to be available after the function call. If that's the case, you need to use the inout
modifier.
Otherwise, if all you need is to modify the parameters inside of the function call, you can explicitly define them as variables on the function definition:
Using in-out parameters
First, change your function declaration to:
func exampleFunction(inout value: String, index: inout Int) -> Bool
Now, an in-out parameter has a value that is passed in to the function, is modified by the function, and then is passed back out of the function, replacing the original value. For this to work, you can't pass a literal into your function, since there would be nowhere to store the modified value afterwards. In fact, it has to be a variable.
You cannot pass a constant or a literal value as the argument, because constants can't be modified. Hence, change your function call to:
var passed = "passedValue"
var index = 2
var b = exampleFunction(&passed, &index)
After the call, both passed
and index
will contain the new values, as modified by the function.
Also, note the &
before each argument when calling the function. It must be there, to indicate the argument can be modified by the function.
Using variable parameters – Removed in Swift 3
In this case, all you need to do is change your function declaration to use variable parameters, as below:
func exampleFunction(var value: String, var index: Int) -> Bool
Changes made to the arguments inside the scope of the function, aren't visible outside the function or stored anywhere after the function call.
How to denote mutable parameters in closures with Swift 2.2?
Closures can mutate inout
arguments just fine:
var str1 = "Pine"
var str2 = "Juniper"
let closure = { (s1: inout String, s2: inout String) -> Void in
s1 = "Oak"
s2 = "Chestnut"
}
closure(&str1, &str2)
print(str1, str2)
The problem you are facing is Array.map
doesn't have a method signature that includes an inout
parameter.
The only way around this that I can think of is to extend Array and add the map
method yourself:
extension Array {
func map<T>(_ closure: (inout T) -> Void) -> Array<T> {
var arr = [T]()
for i in 0..<self.count {
var temp : T = self[i] as! T
closure(&temp)
arr.append(temp)
}
return arr
}
}
var hi = "hi", there = "there", world = "world"
var demoStrings = [hi, there, world]
var result = demoStrings.map { (word: inout String) in
word.remove(at: word.startIndex)
}
print(result) // ["i", "here", "orld"]
How to change $0 to be mutable?
You don't need $0
to be mutable. map
will use whatever value you return, so you can use the last map like this:
.map {$0 == "123" ? "replace" : $0}
When that map
closure is run, whenever $0
matches "123"
, it will return replace
, otherwise it will return the current value.
Mutable parameters on swift closure using Objective-c typedef
Generally you can set stop
using memory
like so:
if !continueFlag { stop.memory = true }
Or, you can do:
stop.memory = ObjCBool(!continueFlag)
Related Topics
Change Color Searchbar Result Icon Swift
How to Get Indexpath When Image Inside Cell Tapped
How to Use Array.Filter to Filter a Class Object Based on a Property
Function Does Not Wait Until the Data Is Downloaded
How to Require That a Protocol Can Only Be Adopted by a Specific Class
Why Do Enums Have Computed Properties But Not Stored Properties in Swift
How to Call a Function When a Swiftui Picker Selection Changes
Transparent Background for Texteditor in Swiftui
Trying to Know When a Window Closes in a MACos Document Based Application
Custom Segue Transition Animation
Draw Scenekit Object Between Two Points
What Versions of Swift Are Supported by What Versions of Xcode
What Are the Supported Swift String Format Specifiers
How to Save a Struct to Realm in Swift
How to Configure Contextmenu Buttons For Delete and Disabled in Swiftui