How is a return value of AnyObject! different from AnyObject

做~自己de王妃 提交于 2019-11-30 10:46:59

TLDR: Treat Foo! as if it were Foo.

Many Cocoa calls include implicitly unwrapped optionals, and their need for it is very likely the reason the feature even exists. Here's how I recommend thinking about it.

First, let's think about a simpler case that doesn't involve AnyObject. I think UIDevice makes a good example.

class func currentDevice() -> UIDevice!

What's going on here? Well, there is always a currentDevice. If this returned nil that would indicate some kind of deep error in the system. So if we were building this interface in Swift, this would likely just return UIDevice and be done with it. But we need to bridge to Objective-C, which returns UIDevice*. Now that should never be nil, but it syntactically could be nil. Now in ObjC, we typically ignore that fact and don't nil-check here (particularly because nil-messaging is typically safe).

So how would we express this situation in Swift? Well, technically it's an Optional<UIDevice>, and you'd wind up with:

class func currentDevice() -> UIDevice?

And you'd need to explicitly unwrap that every time you used it (ideally with an if let block). That would very quickly drive you insane, and for nothing. currentDevice() always returns a value. The Optional is an artifact of bridging to ObjC.

So they invented a hack to work around that (and I think it really is a hack; I can't imagine building this feature if ObjC weren't in the mix). That hack says, yes, it's an Optional, but you can pretend it's not, and we promise it's always going to be a value.

And that's !. For this kind of stuff, you basically ignore the ! and pretend that it's handing you back a UIDevice and roll along. If they lied to you and return nil, well, that's going to crash. They shouldn't have lied to you.

This suggests a rule: don't use ! unless you really need to (and you pretty much only need to when bridging to ObjC).

In your specific example, this works in both directions:

func valueForAttribute(key: String!) -> AnyObject!

Technically it takes an Optional<String>, but only because it's bridged to NSString*. You must pass non-nil here. And it technically returns you Optional<AnyObject>, but only because it's bridged to id. It promises that it won't be nil.

According to the Swift-eBook, which states the following

„Trying to use ! to access a non-existent optional value triggers a runtime error. Always make sure that an optional contains a non-nil value before using ! to force-unwrap its value.“

I would answer to your first two questions with Yes.

Do I need to include an if let block when examining it...

No, this is not necessary.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!