Reputation: 16430
Let's take these two simple classes to show my problem:
class Object{
var name:String?
// keep it simple... and useless
}
class TestClass {
var objects:AnyObject[]?
func initializeObjects (){
objects?.insert(Object(), atIndex:0) // Error
objects?.insert(Object(), atIndex:1) // Error
objects?.insert(Object(), atIndex:2) // Error
}
}
With this implementation I get 3 errors Could not find member 'insert'
where I try to add object into the objects
array.
Now, if I remove the optional from objects
definition and the optional chain in initializeObjects
it works with no problem (here the working code)
class Object{
var name:String?
}
class TestClass {
var objects:AnyObject[] = AnyObject[]() // REMOVE optional and initialize an empty array
func initializeObjects (){
objects.insert(Object(), atIndex:0) // Remove Opt chaining
objects.insert(Object(), atIndex:1) // Remove Opt chaining
objects.insert(Object(), atIndex:2) // Remove Opt chaining
}
}
I can't understand what is wrong in the first implementation.
I thought it checks with objects?
if objects
is not nil
and at this point it adds an element using insert:atIndex:
. But I'm probably wrong -.-
Upvotes: 2
Views: 1282
Reputation: 130122
Arrays in Swift are structs and structs are value types.
Optionals in Swift are actually enums (Optional<T>
or ImplicitlyUnwrappedOptional<T>
).
When you are unwrapping an optional (implicitly or explicitly) of a value type, what you get is actually a constant copy of the struct. And you can't call mutating
methods on a constant struct.
Executing objects?.insert(Object(), atIndex:0)
basically means this:
if let tmp = objects {
tmp.insert(Object(), atIndex:0)
}
As a workaround, you need to assign the unwrapped value to a variable and then assign the variable back to your optional property. That's how value types work.
This is reproducible for any struct, not only Arrays:
struct S {
var value: Int = 0
}
var varS: S = S()
varS.value = 10 //can be called
let constS: S = S()
constS.value = 10 //cannot be called - constant!
var optionalS: S? = S()
optionalS?.value = 10 //cannot be called, unwrapping makes a constant copy!
//workaround
if optionalS {
var tmpS = optionalS!
tmpS.value = 10
optionalS = tmpS
}
Some relevant discussion here: https://devforums.apple.com/thread/233111?tstart=60
Upvotes: 5