简体   繁体   中英

Implicitly unwrapped optional in Apple methods

I'm trying to switch from Objective-C to Swift. I don't understand the point of declaring a function to return a AnyObject! instead of AnyObject? .

For example:

func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject!

Why does this method return an implicitly unwrapped optional and not a simple optional? I get the AnyObject part, but what's the point of allowing us to avoid using the ! to unwrap the optional if it may be nil? (thus crashing the app, even if it's very unlikely)

What am I missing?

Is it just a convenient way to use the return value of this method without having to use ! or is there something else I can't see? In which case, is the program doomed to crash if it returns nil ?

It sounds to me that:

  • AnyObject means 100% chance to return something
  • AnyObject? means 50% chance to return nil, you should always check for nil
  • AnyObject! means 99% percent chance to return something not nil, checking for nil is not required

Also, I assume there is some kind of link with Objective-C/NSObject classes, but can't figure out what...

Thank you

The frequency of implicitly unwrapped optionals (IUOs) in Cocoa APIs is an artifact of importing those APIs to Swift from ObjC.

In ObjC, all references to objects are just memory pointers. It's always technically possible for any pointer to be nil, as far as the language/compiler knows. In practice, a method that takes an object parameter may be implemented to never allow passing nil, or a method that returns an object to never return nil. But ObjC doesn't provide a way to assert this in an an API's header declaration, so the compiler has to assume that nil pointers are valid for any object reference.

In Swift, object references aren't just pointers to memory; instead they're a language construct. This gets additional safety by allowing us to assert that a given reference always points to an object, or to use optionals to both allow for that possibility and require that it be accounted for.

But dealing with optional unwrapping can be a pain in cases where it's very likely that a reference will never be nil. So we also have the IUO type, which lets you use optionals without checking them for nil (at your own risk).

Because the compiler doesn't know which references in ObjC APIs are or aren't safely nullable, it must import all object references in ObjC APIs (Apple's or your own) with some kind of optional type. For whatever reason, Apple chose to use IUOs for all imported APIs. Possibly because many (but importantly not all!) imported APIs are effectively non-nullable, or possibly because it lets you write chaining code like you would in ObjC ( self.view.scene.rootNode etc).

In some APIs (including much of Foundation and UIKit), Apple has manually audited the imported declarations to use either full optionals (eg UIView? ) or non-optional references ( UIView ) instead of IUOs when semantically appropriate. But not all APIs have yet been audited, and some that have still use IUOs because that's still the most appropriate thing for those APIs to do. (They're pretty consistent about always importing id as AnyObject! , for example.)

So, getting back to the table in your question: it's better not to think in terms of probabilities. When dealing with an API that returns...

  • AnyObject : always has a value, cannot be nil.
  • AnyObject? : may be nil, must check for nil
  • AnyObject! : no guarantees. Read the header, documentation, or source code (if available) for that API to find out if it can really be nil in your situation and treat it accordingly. Or just assume it can be nil and code defensively.

In the case of instantiateViewControllerWithIdentifier , there are two options you might call equally valid:

  1. Say you're guaranteed to never get nil because you know you put a view controller in your storyboard with that identifier and you know what class it is.

     let vc = storyboard.instantiateViewControllerWithIdentifier("vc") as MyViewController // do stuff with vc 
  2. Assume your storyboard can change in the future and set yourself up with a meaningful debug error in case you ever break something.

     if let vc = storyboard.instantiateViewControllerWithIdentifier("vc") as? MyViewController { // do something with vc } else { fatalError("missing expected storyboard content") } 

AnyObject means it cannot be nil

AnyObject? and AnyObject! both mean they can be nil . There is no "probability" difference between them. The only difference between them is with AnyObject you don't have to explicitly force-unwrap it, for convenience. With both of them you can use optional binding and optional chaining if you want to use it safely.

Object pointer types in Objective-C are by default imported into Swift as implicitly-unwrapped optionals ( ! ). That is because all Objective-C pointers could potentially be nil (there is no way to tell automatically from an Objective-C declaration whether a particular object-pointer can be nil or not), but maybe in a particular API it can't nil , so they don't want to burden you with explicitly unwrap if if this is the case.

Apple has been "auditing" its APIs to manually add information for whether each object-pointer type can be nil or not, so over time you will see AnyObject! in the API change to AnyObject or AnyObject? . This process takes lots of human effort and is slow and ongoing.

Regarding the following function declarations:

#1) func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject
#2) func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject!
#3) func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject?

#2 and #3 are logically the same, however, as per p 40 of iBook, the answer is, much like Rickster's answer, a matter of semantics. iBook》第40页,答案就像Rickster的答案一样,是一个语义问题。

AnyObject! and AnyObject? are treated the same behind the scenes (they are initially set to nil and can be nil at any point), however it is the intention that once AnyObject! has been set it should no longer point to nil .

This allows one to avoid using the forced optional unwrapping syntax ( ! ) and simply use implicitly unwrapped optionals (variables declared with ! ) to access the value contained within the assigned variable.

An example from the book:

let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark

As applied to the above:

func instantiateViewControllerWithIdentifier(identifier: String) -> AnyObject! {
    return nil
}

let optionalVC: AnyObject! = instantiateViewControllerWithIdentifier("myVC")
self.viewController.presentViewController(optionalVC as UIViewController, animated: true, completion: nil)

// The above is perfectly valid at compile time, but will fail at runtime:
// fatal error: unexpectedly found nil while unwrapping an Optional value

Thus, a function that declares the return value as AnyObject! return nil however, should avoid doing so to avoid breaking the implied contract of implicitly unwrapped optionals 返回nil但是应该避免这样做,以免破坏隐式展开的可选对象隐含契约

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM