Swift: Strange Behavior About Unwrapping

Swift: strange behavior about unwrapping

You need an explicit unwrap because you are using concatenation operator. Concatenation is not defined for optional strings, so you need to add exclamation point to unwrap the values. You should be able to avoid it if you use string interpolation, like this:

cell!.detailTextLabel.text = "\(child.year) \(child.make) \(child.model)"

Note: if the initializer that you show is the only one available, you should be able to make your strings non-optional.

Implicitly unwrapped optional strange behaviour with map

There are two versions of map: one that operates on the array and one that operates on optionals.

Your grid is an optional, and even though it is an implicitly unwrapped optional (IUO) it is still an optional. Swift will treat an IUO as an optional whenever possible and only force unwraps the value when the unwrapped type is needed.

Because of this, the optional version of map is used instead of the one you expect. By explicitly unwrapping grid with !, you then allow the desired version of map to be used.

How does map work on an optional?

When map is applied to an optional, it applies the closure to the unwrapped value. If the optional is nil, nothing happens.

So, grid is unwrapped and becomes $0, and the closure returns $0[0] which is the first row of the grid.


Note: In Xcode, if you option-click on map in each statement you will see that the first says:

Evaluates the given closure when this Optional instance is not nil,
passing the unwrapped value as a parameter.

and the second one:

Returns an array containing the results of mapping the given closure
over the sequence’s elements.

Strange behavior of Swift

The '!' indicates forced unwrapping of an optional. However it's type is still optional and therefore can be nil.

(You're just telling the compiler that you guarantee it won't become nil.)

A line like...

let v: Int = myVar

...at the end of your code will get you the crash you expect.


Another example is the way outlets are handled. They are typically defined such as:

@IBOutlet weak var detailDescriptionLabel: UILabel!

Before viewDidLoad is called, it is nil. Again, the '!' is just a promise to the compiler that it won't be used until it contains a valid reference.

The implicit unwrapping of an optional boolean

The first test is checking whether aBoolean stores a value rather than nil, which it does:

if aBoolean {
"Hum..." // "Hum..."
else {
"Normal"
}

The second test is checking against the actual boolean value stored in aBoolean, which is false:

if aBoolean! {
"Hum..."
} else {
"Normal" // "Normal"
}

This is Illustrated in the Swift book in the "Implicitly Wrapped Optionals" section. I think the implicit unwrapping just doesn't apply with if-statements. I agree it is strange but here is the Apple example:

You can still treat an implicitly unwrapped optional like a normal optional, to check if it contains a value:

let assumedString: String! = "An implicitly unwrapped optional string."

if assumedString {
println(assumedString)
}
// prints "An implicitly unwrapped optional string."

Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l

For-in loop goes one-too-far and finds 'nil' while unwrapping

Basically you've done something you're not supposed to do, and thus you've stumbled on something that's probably a compiler bug (because the compiler didn't stop you). Very nice!

Now the solution. You are evidently trying to unwrap with your Int!. To unwrap safely, use for case syntax:

let list:[Int?] = [1,2,3,4,5,6,7]
for case let x? in list
{
print(x) // 1, 2, 3, 4, 5, 6, 7
}

Another way (same result):

let list:[Int?] = [1,2,3,4,5,6,7]
list.flatMap{$0}.forEach{print($0)} // 1, 2, 3, 4, 5, 6, 7

Double optionals (??) and double unwrapping (!!) with Button text in Swift

This is because of AnyObject. The first ? is for "is it an object that responds to the titleLabel method?", and the second ? is for "is the title label nil?"

If you are only hooking up a button from Interface Builder, you can use

@IBAction func buttonTapped(sender: UIButton)

Why does implicitly unwrapped optional not unwrap in dictionary of type [String : Any]

Under the rules set out by SE-0054, IUOs are only force unwrapped in contexts that demand their unwrapped type. In your case, the IUO doesn't need to be force unwrapped in order to be coerced to Any (as Any can represent any value), so it isn't.

This behaviour is discussed in more detail in these Q&As:

  • Swift 3 incorrect string interpolation with implicitly unwrapped Optionals
  • Implicitly unwrapped optional assign in Xcode 8

The fact that you end up with an ImplicitlyUnwrappedOptional value in your dictionary is legacy behaviour that has been removed in the latest Swift snapshots, in the future you will end up with an Optional value instead (as IUO is no longer a type).

One important thing to note here however (that I'm sure will trip up people) is that the printing of IUOs got changed in 4.1.

In Swift 4.0.3, your example prints like this:

var aString: String! = "hello"
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": hello]

giving you the illusion that the IUO was force unwrapped when coerced to Any. This however is just how IUOs were printed in Swift 4.0.3 – if they had a value, then they would print as that value, otherwise they would print as nil:

var aString: String! = nil
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": nil]

The reason why this changed in Swift 4.1 is that ImplicitlyUnwrappedOptional's conformance to Custom(Debug)StringConvertible was removed in this commit in order to make progress towards removing the type itself. So now ImplicitlyUnwrappedOptional values get printed using Swift's default printing mechanism (using reflection).

So, in a dictionary, you get the IUO's default debugDescription, which looks like this:

let aString: String! = "hello"
let params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("hello")]

If you had printed it on its own, you would get its default description, which looks like this:

let aString: String! = "hello"
print(aString) // some("hello")

This is because in Swift 4.1, the ImplicitlyUnwrappedOptional type is implemented in the same way as Optional, an enumeration with two cases:

public enum ImplicitlyUnwrappedOptional<Wrapped> : ExpressibleByNilLiteral {
// The compiler has special knowledge of the existence of
// `ImplicitlyUnwrappedOptional<Wrapped>`, but always interacts with it using
// the library intrinsics below.

/// The absence of a value. Typically written using the nil literal, `nil`.
case none

/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)

// ...
}

For an IUO with a payload value, Swift's default reflection will therefore print it as the case some containing the wrapped value.

But this is only temporary; the IUO type is currently (in Swift 4.1) deprecated, however it will be removed in Swift 4.2. The compiler was internally using the IUO type in quite a few places, which took quite a bit of work to remove. Therefore in 4.2 you'll have actual Optional values in your dictionary, which will print like Optional("hello").



Related Topics



Leave a reply



Submit