Why does Swift 2 favor forced unwrap over optionals?

旧时模样 提交于 2019-12-02 09:30:38

I don't really know what you mean when you write that you no longer see Xcode complaining that "certain things need optionals. Now it is always forced unwrapped". These two sentences contradict eachother:

  • You may have non-optional variables as much as you wish, but optional can really nice once you get to know all the benefits of using them.
  • If you have a property that is not an optional, then it won't need, by definition, any unwrapping.

Perhaps what you ment was that Xcode seemingly complains less often when you actually do force unwrap optionals, or, as a bad habit of Xcode, prompts you to force unwrap things to avoid compile time errors. This is generally because Xcode cannot know at compile time that you just wrote code that will break your app at runtime.

Xcode may seemingly at times have only one single purpose with its "smart hints": namely to absolve compile time errors. If you try to assign the value of a String? type (optional String) to String type, Xcode will prompt you with a compiler error and ask if you ment to add the forced unwrapping operator !. Smart Xcode, you say? Meh, Xcode is good for many things, but deciding how you unwrap your optionals is, not yet anyway, one of them. So even with Xcode prompting you for all kinds of things: if you can use optional chaining, do.

There might be exception, of course. For the controller part of the MVC design pardigm, you usually use the as! operator to do "forced conversion" (casting), with Xcode sometimes telling you to explicitly to use as! instead of as, e.g. "Did you mean as! ... ?". In these situations, Xcode can sometimes actually know what its doing and infer that you are trying to cast, as an example, a custom UIViewController class instance to type UIViewController, i.e., to it's parent class. I'd say that this is perhaps one of the few times I use the "forced" marker ! without go-arounds; forced conversion to types I know with 100% certainty to be castable.

But let's leave the subject of type conversion/casting and move on to optional types, wrapping, and optional chaining, in general.


Generally, you should avoid force unwrapping unless you explicitly knows that the entity you unwrap will be non-nil. For general class properties, variables and so on, given that you state this question the way you do, I'll give your the following advice:

If you can use conditional unwrapping (if-let, guard-let, nil coalescing operator ??), then don't use forced unwrapping (!).

Below follows an example of the dangers of forced unwrapping. You can treat the first if clause (if arc4random...) as any smaller or larger segment of some program you've written with imperative programming techniques: we don't really know in detail just how 'name' will turn out until runtime, and our compiler can't really help us out here.

var name : String?

/* 'name' might or might not have a non-nil 
    value after this if clause */
if arc4random_uniform(2) < 1 {
    name = "David"
}

/* Well-defined: us an if-let clause to try to unwrap your optional */
if let a = name {
    print("Hello world "+a)
        /* Very well-behaved, we did a safe
           unwrap of 'name', if not nil, to constant a */

    print("Hello world "+name!)
        /* Well... In this scope, we know that name is, 
           for a fact, not nil. So here, a force unwrap
           is ok (but not needed). */
}

let reallyGiveMeThatNameDammit = name!
    /* NOT well-defined. We won't spot this at compile time, but
       if 'name' is nil at runtime, we'll encounte a runtime error */

I recommend you to read up on optional chaining, a key subject in Swift.

do you mean that cocoa stuff? do you mean implicitly unwrapped?

protocol AnyObject { ... }

The protocol to which all classes implicitly conform.

When used as a concrete type, all known @objc methods and properties are available, as implicitly-unwrapped-optional methods and properties respectively, on each instance of AnyObject. For example:

class C {
  @objc func getCValue() -> Int { return 42 }
}

// If x has a method @objc getValue()->Int, call it and
// return the result.  Otherwise, return nil.
func getCValue1(x: AnyObject) -> Int? {
  if let f: ()->Int = x.getCValue { // <===
    return f()
  }
  return nil
}

// A more idiomatic implementation using "optional chaining"
func getCValue2(x: AnyObject) -> Int? {
  return x.getCValue?() // <===
}

// An implementation that assumes the required method is present
func getCValue3(x: AnyObject) -> Int { // <===
  return x.getCValue() // x.getCValue is implicitly unwrapped. // <===
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!