Implicitly Unwrapped Optional Made Immutable

Implicitly unwrapped optional made immutable

Update:

This has been fixed in Xcode Beta 5 with one small caveat:

var list: [Int]! = [1]
list.append(10)

var number: Int! = 1
number! += 2
number += 2 // compile error

The array works as expected, but it seems that right now the integer still requires an explicit unwrap to allow using +=


Currently, this is just the nature of Optionals (whether implicitly unwrapped or not). The unwrap operator returns an immutable value. This is likely to be fixed or a better solution will be provided in the future.

The only way around it for now is to wrap the array in a class:

class IntArray {
var elements : [Int]
}

Why does a `nil` implicitly unwrapped optional print `nil` and not crash?

That does not crash because print accepts Any as the first parameter. Are implicitly unwrapped optionals a kind of Any? Yes they are! Anything is Any. There is no need to unwrap the optional. An implicitly unwrapped optional can be used in a place where Any is expected without unwrapping the optional.

That could potentially be confusing because now you have something with type Any, which doesn't look like it's optional, but it is an optional under the hood. To avoid this, Swift will output a warning telling you that you are implicitly coercing whatever optional type to Any.

You need to use ! to force unwrap it here:

print(x!)

Why create Implicitly Unwrapped Optionals , since that implies you know there's a value?

Consider the case of an object that may have nil properties while it's being constructed and configured, but is immutable and non-nil afterwards (NSImage is often treated this way, though in its case it's still useful to mutate sometimes). Implicitly unwrapped optionals would clean up its code a good deal, with relatively low loss of safety (as long as the one guarantee held, it would be safe).

(Edit) To be clear though: regular optionals are nearly always preferable.

Are implicitly unwrapped optionals truly optionals?

This is a known bug in the swift compiler. Hamish says in a comment this is fixed in a Swift 4.1 snapshot, so it may be fixed in the next Xcode release (9.3).

You can work around this by getting rid of the implicitly-unwrapped optional (IUO), which should be avoided anyway. Depending on why it's currently an IUO, either:

var str: String?
func someFunc(_ x: inout String?) {}
someFunc(&str)

or

var tmp: String?
func someFunc(_ x: inout String?) {}
someFunc(&tmp)
let str = tmp!

I strongly recommend the first, avoid force-unwrapping unless absolutely necessary.

Implicitly unwrapped optional var destroyed by compiler before end of scope?

But as a programmer we are used to local variables inside of functions being available until leaving the scope.

This hasn't been the case since ARC was first released for ObjC. ARC has always had the option to release objects after their last use (and very often makes use of this). This is by design, and is not a bug in Swift (or in ObjC, where it's also true).

In Swift, if you want to extend the lifetime of an object beyond its last use,withExtendedLifetime is explicitly for this purpose.

var someObject: SomeClass! = SomeClass()

withExtendedLifetime(someObject) {
someObject.doSth()
print("done")
}

Keep in mind that it is legal for objects to have balanced retain/autorelease calls on them, which may cause them to outlive their scope as well. This is much less common in Swift, but still legal and happens if you pass a Swift object to ObjC (which can happen in many places you may not expect).

You should be very careful relying on when deinit will be called. It can surprise you, and isn't even promised in all cases (for example, deinit is not called during program quit on Mac, which tends to surprise C++ developers).

IMO performAsyncSyncTask is a dangerous pattern, and should be redesigned with clearer ownership. I don't do enough RxSwift work to immediately redesign it, but blocking the whole thread on a DispatchSemaphore seems the wrong way to integrate with any reactive system. Threads are a finite resource, and this forces the system to create more while this one is blocked doing nothing.

Why can't I pass an implicitly unwrapped optional as an UnsafeMutablePointer?

I'm afraid this is another lingering bug around implicitly unwrapped optionals (IUOs).

It has already been fixed though (almost certainly as a result of the recent-ish work to completely remove IUOs from the type system) – it compiles in the latest dev snapshots, and therefore will make it into 4.2 (final re-branch from master is April 20).

Until 4.2 rolls around, one possible workaround would be to use a forwarding computed variable in order to treat the IUO as a strong Optional type:

class SomeClass {}

var obj: SomeClass!
var _optionalObj: SomeClass? {
get { return obj }
set { obj = newValue }
}

func pointerFunc(_: UnsafeMutablePointer<SomeClass?>) {}
pointerFunc(&_optionalObj)

(that is, assuming you're okay with having the pointer point to a temporary value – i.e you're not relying on the pointer value being stable or unique, such as you would be if this were to be used, for example, as an associated object key)

Implicit Unwrapping and Initializers

I thought one point of an implicitly unwrapped optional was that it had a valid nil value from its declaration, and that this did not count as the one and only assignment a let statement permits.

Unfortunately, it counts. This behavior is fixed into current style after this update:

Xcode Release Notes > Xcode 6.3 > Swift Language Changes

You may have found some workarounds, but I think this would be very near to what you want:

class MyView: UIView          {
private(set) var myLayer: MyLayer!

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.myLayer = MyLayer(owner: self)
self.layer.addSublayer(self.myLayer) }
}

What is the point of using implicitly unwrapped optional in this example?

All your concerns about misleading documentation are right.

Also, please note that in Swift 2.2 returning early from a failable initializer does work, even before all properties are initialized:

class Product {
let name: String
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}

Another change is that deinit is not called any more when returning nil from the failable initializer.

From Xcode 7.3 beta 2 Release Notes:

Inside a class, a designated initializer that is either failable (init?()) or throwing (init() throws) is allowed to exit before initializing all stored properties and calling super.init(). This behavior is supported, making designated initializers more consistent with convenience initializers. Convenience initializers can also fail before performing a self.init() delegation.

Implicitly unwrapped multiplication and division

You can make the *= operator work by explicitly unwrapping the optional first like so:

func toOunces() {
weight! *= 0.035274
}

You can see why this is looking at how *= is defined.

func *=(inout lhs: Double, rhs: Double)

An implicitly unwrapped optional can't be passed as an inout parameter because it is a wrapper around a Double (or nil.)

Non inout arguments can be unwrapped automatically because the function just needs the value which can be extracted from the optional automatically rather than a reference to the actual value. That's why the * operator works for Double! which is defined as such.

func *(lhs: Double, rhs: Double) -> Double

Adding the ! after weight in your code changes it from passing a reference to the optional to passing a reference to the Double encased in the Optional. If *= had a normal function syntax and we remove some sugar it might look like this:

func multiplyAssign (inout lhs: Double, rhs: Double){
lhs = rhs * lhs
}
var weight: ImplicitlyUnwrappedOptional<Double> = 0.0
multiplyAssign(&weight, 10.0) //trying to pass reference to an ImplicitlyUnwrappedOptional<Double> (error)
multiplyAssign(&(weight!), 10.0) //passing a reference to a Double


Related Topics



Leave a reply



Submit