This question already has answers here:
Mutating function inside class
(4 answers)
Closed 3 years ago.
I am missing something about the mutability concept in Swift. I normally use objects, not structs, to get observability, so the value semantics are still new to me.
struct Game {
var map: [[Int]]
So here I have declared map as mutable. So why in a method like this...
mutating func createPlayer() {
// emptyLocation -> (Int, Int)
let (X,Y) = emptyLocation()
map[X][Y] = .player
}
...do I have to use the mutating? Yes, the function is mutating, but the original struct is declared as such. It seems that practically every func will be mutatingin practice, which seems to defeat the purpose of the markup.
Is there some other way I should be doing this? Is the common use of mutating indicating a performance/memory issue I should be avoiding?
Update: I was rather upset at the way the internal state of the struct "leaked out" to the surrounding code; if you declare a member var inside the struct then it has to be outside as well, even if you never changed. This violates every concept of encapsulation I can think of. So I changed struct to class, removed all the mutating, and was done with it. I get the idea, but I'm not sure I fully understand the implementation. It seems, to this Swift-noob, that the mutating is something the compiler can determine without me telling it - is the member declared var?, does the func actually mutate it? etc.
Yes it is the default behaviour for a struct or enum that instance methods can not modify a property since they are value type. So you need to use mutating to override this behaviour.
The way you define your property, var or let, is still relevant for if you can change it from a mutable instance method or directly or not.
Since your property is not private you can still do
var g = Game(map: [[1]])
g.map.append([2])
In Swift structs have value semantics, ie they behave as if they are values even though some of them like String and Array are implemented as references for performance reasons. When you mutate such a struct the compiler may actually have to make a copy if you are not the only owner of the struct; this is known as copy on write and it is the possible performance issue that mutating indicates.
Related
I have a collection of objects in a Set. The objects' type follows GeneratorType, so I have a mutating method next. Although I can mutate the set by adding/removing elements, I don't know how to mutate an element. Using both for-in statement and the forEach method give errors about the element being immutable. Is the mutability of Swift containers shallow?
Is there a way to call a mutating method on a contained element? Does it work for other collection/sequence types besides Set? (My changes would change each object's hash, and maybe I'm not allowed to affect a set like that.)
The situation is similar to that of mutating one of a dictionary's values, where that value is a value type. You can't do it, even if your reference to the dictionary is a var reference. Typically, you'll just pull the value out, mutate it, and put it back again:
var d = ["key":"hell"]
var val = d["key"]!
val.append(Character("o"))
d["key"] = val
Your Set works similarly.
var s = Set(["hell"])
var val = s.remove("hell")!
val.append(Character("o"))
s.insert(val)
There are other ways to notate that, but in effect they amount to the same thing.
The thing to keep in mind here is that no value type, e.g. a struct, is truly mutable. We speak of it as if it were, and so does Swift with its mutating functions, but mutation actually consists of assigning a new value back into the reference. That is why the reference must be a var. There is thus no such thing as mutation in place for any value type.
Now, of course, with a reference type, e.g. a class, the situation is completely different:
let s = Set([NSMutableString(string:"hell")])
s.first!.appendString("o")
s // {"hello"}
I'm having a difficulty reconciling my admittedly incomplete understanding of optionals and this from the Swift 2.1 documentation:
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
I'd like to be able to do something like:
struct Name {
var firstName = "Flintstone First"
var middleName: String?
var lastName = "Flintstone"
}
var wilmaHusband: Name
wilmaHusband.firstName = "Fred"
where middleName may be nil. However, quite understandably according to that part of the Swift documentation, I encounter the error Struct wilmaHusband must be completely initialized before a member is stored...
Is what I'm trying to do--make a member potentially nil in a structure (or class) impossible in Swift? If so, it seems one seeming advantage of optionals--that they may hold the kind of data that may or may not be present in a structured object (such as a middle name in a name construct)--is lost.
I feel like I'm missing something fundamental here in my understanding of optionals, and I apologize in advance for my naïveté.
Since all members of the struct have an initial value
(including var middleName: String? which – as an optional – is
implicitly initialized to nil), you can create a variable of that
type simply with
var wilmaHusband = Name()
I understand that generally structs in swift are pass-by-value. I use a struct for encapsulating a few bits of information add the struct to a set and later change small bits of its values. However; I seemed to have fallen into an issue whereby the structs are not updating correctly even though I have sprinkled the keyword inout everywhere the parameter requires a struct. My gut instinct was to allocate memory for the struct and refer to it in the set by it's pointer. Would it make sense to simply use a class even though all I need is a list of values that can change.
If you need reference semantics, then absolutely use a class. If you want to be able to modify your object both in a data structure as well as other places, a class is what you need. It is perfectly reasonable to use a class just to get reference semantics.
Also so you know, inout on a function parameter does not actually mean pass by reference. What is actually happening is a copy of your struct is made by the function. This copy is then modified in the function and later copied back to the original variable.
Without your code, I can't see what you're doing wrong, but it works for me - Playground minimal example:
struct Grimxn {
var first: Int
var second: Int
}
func modify(inout v: Grimxn) {
v.first++
v.second--
}
var a = Grimxn(first: 1, second: 2)
print("\(a)") // "Grimxn(first: 1, second: 2)\n"
modify(&a)
print("\(a)") // "Grimxn(first: 2, second: 1)\n" - as required?
I certainly wouldn't want to use pointers - why do that in Swift - use C!
I'm new to Swift and iOS coding and have been working on writing my first app. While my programming background is pretty significant, I come from a Python and C# background where pretty much anything can be None or null and it's up to the user to check at runtime for a null. I'm finding this whole concept of "nullable vs. non-nullable types" or "optional types" in Swift to be confusing.
I understand that the core concept is that a variable declared as a type like myObject cannot be set to nil. However, if I define it as type myObject? then the value can be set to nil.
The problem is that, when I look at my code designs, it feels like everything will have to be "nullable" in my code. It feels like this either means I'm not thinking correctly with how my code should run, or that I'm missing some crucial piece of understanding.
Let's take the simplest example of something I am confused about. Suppose I have two classes - one that stores and manages some sort of data, and another that provides access to that data. (An example of this might be something like a database connection, or a file handle, or something similar.) Let's call the class containing data myData and the class that works with that data myObject.
myObject will need a class-level reference to myData because many of its methods depend on a local reference to the class. So, the first thing the constructor does is to generate a data connection and then store it in the local variable dataConnection. The variable needs to be defined at the class level so other methods can access it, but it will be assigned to in the constructor. Failure to obtain the connection will result in some sort of exception that will interfere with the very creation of the class.
I know that Swift has two ways to define a variable: var and let, with let being analogous to some languages' const directive. Since the data connection will persist throughout the entire life of the class, let seems an obvious choice. However, I do not know how to define a class-level variable via let which will be assigned at runtime. Therefore, I use something like
var dataConnection: myData?
in the class outside any functions.
But now I have to deal with the nullable data type, and do explicit unwrapping every time I use it anywhere. It is frustrating to say the least and quite confusing.
func dealWithData() {
self.dataConnection.someFunctionToGetData() <- results in an unwrapping error.
self.dataConnection!.someFunctionToGetData() <- works.
let someOtherObjectUsingData: otherObject = self.getOtherObject() <- may result in error unless type includes ?
someOtherObjectUsingData.someMethod(self.dataConnection) <- unwrap error if type included ?
var myData = self.dataConnection!
someOtherObjectUsingData.someMethod(myData) <- works
}
func somethingNeedingDataObject(dataObject: myData?) {
// now have to explicitly unwrap
let myDataUnwrapped = myData!
...
}
This just seems to be an extremely verbose way to deal with the issue. If an object is nil, won't the explicit unwrap in and of itself cause a runtime error (which could be caught and handled)? This tends to be a nightmare when stringing things together. I've had to do something like:
self.dataConnection!.somethingReturningAnObject!.thatObjectsVariable!.someMethod()
var myData? = self.dataConnection
var anotherObject? = myData!.somethingReturningAnObject
...
The way I'm used to doing this is that you simply define a variable, and if it is set to null and you try to do something with it, an exception (that you can catch and handle) is thrown. Is this simply not the way things work anymore in Swift? This has confused me sufficiently that just about every time I try to compile an app, I get tons of errors about this (and I just let Xcode fix them). But this can't be the best way to deal with it.
Do I have to consistently deal with wrapping and unwrapping variables - even those which are expected to never be null in the first place but simply can't be assigned at compile time?
However, I do not know how to define a class-level variable via let which will be assigned at runtime.
This part is easy. Just use let instead of var. With Swift 1.2 and later, you can delay the actual assignment of a let. The compiler is smart enough to do flow analysis and make sure it's assigned once, and only once, in all paths. So in the case of a class-wide let, the assignment can also happen in the constructor.
But now I have to deal with the nullable data type, and do explicit unwrapping every time I use it anywhere.
But this is what implicitly unwrapped Optionals are for. For example, StoryBoard defines all #IBOutlets as implicitly unwrapped, because the semantics are very clear: upon entrance to viewDidLoad() and everywhere after, unwrapping is safe. If you can prove clear semantics to yourself, you can do the same.
So you have roughly 4 choices:
A) declare at class level as implicitly unwrapped:
let dataConnection: MyData!
And be forced to initialize it in the constructor:
init() {
let whateverObj = someInitialCalculation()
dataConnection = whateverObj.someWayOfGettingTheConnection()
}
And from then on you don't need the '!'; it should be clear that implicit unwrap is always safe.
B) Initialize it right in its declaration if its initialization is reliable and sensible at that point, allowing you to forgo the entire concept of Optionals:
let dataConnection = SomeClass.someStaticMethod()
C) Declare at class level as a var, as implicit optional:
var dataConnection: MyData!
You won't have to init it in the constructor; let it be nil until its value can/should be computed. You still need some flow analysis to prove after a certain point, as in the case of #IBOutlets, accessing it will always be valid
D) The most 'unpredictable' case. Declare it as an explicit optional, because throughout the lifecycle of the class, the data connection will come and go:
var dataConnection: MyData?
func someMethodThatHandlesData() {
if let dC = dataConnection {
dc.handleSomeData()
}
else {
alert("Sorry, no data connection at the moment. Try again later.")
}
}
I think you're imagining that Swift always forces you down path D).
As far as your spaghetti-string code, you want to look into Optional Chaining, and only need to check the end result for nil.
According to the Swift Programming Language reference, Dictionary instances are copied whenever they are passed to a function/method or assigned to a constant or variable. This seems inefficient. Is there a way to efficiently share the contents of a dictionary between two methods without copying?
It's true the documentation says that but there are also various notes saying it won't affect the performance. The copying will be performed lazily - only when needed.
The descriptions below refer to the “copying” of arrays, dictionaries, strings, and other values. Where copying is mentioned, the behavior you see in your code will always be as if a copy took place. However, Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so. Swift manages all value copying to ensure optimal performance, and you should not avoid assignment to try to preempt this optimization.
Source: Classes & Collections
Meaning - don't try to optimize before you actually encounter performance problems!
Also, don't forget that dictionaries are structures. When you pass them into a function, they are implicitly immutable, so no need for copying. To actually pass a mutable dictionary into a function, you can use an inout parameter and the dictionary won't be copied (passed by reference). The only case when a mutable dictionary passed as a parameter will be copied is when you declare the parameter as var.
You always have the option to define a custom, generic class with a Dictionary attribute:
class SharedDictionary<K, V> {
var dict : Dictionary<K, V>
// add the methods you need, including overloading operators
}
Instances of your SharedDictionary will be passed-by-reference (not copied).
I actually talked to someone on the Swift team today about "pass by reference" in Swift. Here is what I got:
As we all know, struct are pass by copy, classes are pass by
reference
I quote "It is extremely easy to wrap a struct in a class.
Pointing to GoZoner's answer.
Even though though a struct is copied, any classes defined in
the struct will still be passed by reference.
If you want to do traditional pass by reference on a struct, use
inout. However he specifically mentioned to "consider adding in
another return value instead of using inout" when saying this.
Since Dictionary defines KeyType and ValueType as generics:
struct Dictionary<KeyType : Hashable, ValueType>
I believe this means that if your KeyType and ValueType are class objects they will not be copied when the Dictionary itself is copied, and you shouldn't need to worry about it too much.
Also, the NSDictionary class is still available to use!
As other said "Swift only performs an actual copy behind the scenes when it is absolutely necessary to do so." so performance should not be a big problem here. However you might still want to have a dictionary passed by reference for some other reasons. In that case you can create a custom class like below and use it just like you would use a normal dictionary object:
class SharedDictionary<K : Hashable, V> {
var dict : Dictionary<K, V> = Dictionary()
subscript(key : K) -> V? {
get {
return dict[key]
}
set(newValue) {
dict[key] = newValue
}
}
}
Trust the language designers: the compiler is usually smarter than you think in optimizing copies.
You can hack around this, but I don't frankly see a need before proving it's inefficient.