为什么 UIKit 不喜欢 Swift 3 个可选项?

Why UIKit doesn't like Swift 3 optionals?

以下 Swift 3 代码崩溃。通过删除显式可选类型或强制展开 view 可以轻松解决崩溃。谁能解释一下为什么这段代码会崩溃?

let view: UIView? = UIView() // note the explicit *optional* type
_ = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0.0, constant: 44.0)

注意:它不会用 Swift 2.3 或更低版本

编译

NSLayoutConstraint(item:, attribute:, relatedBy:, toItem:, attribute:, multiplier:, constant:) 有一个 item 参数类型为 Any:

public convenience init(item view1: Any, attribute attr1: NSLayoutAttribute, relatedBy relation: NSLayoutRelation, toItem view2: Any?, attribute attr2: NSLayoutAttribute, multiplier: CGFloat, constant c: CGFloat)

但是从崩溃中你可以了解到参数实际上只能接受 UIViewUILayoutGuide:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'NSLayoutConstraint for Optional(UIView: 0x7fa0fbd06650; frame = (0 0; 0 0); layer = CALayer: 0x60800003bb60): Constraint items must each be an instance of UIView, or UILayoutGuide.'

编译器无法在编译期间检查 item 的类型。它被定义为接受任何东西。但是在我们无法访问的实现细节中,该方法仅接受非可选的 UIViews 或 UILayoutGuides.

所以只需添加一个guard语句:

let view: UIView? = UIView()
guard let view = view else { // Proceed only if unwrapped
  fatalError()
}
let _ = NSLayoutConstraint(item: view, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 0.0, constant: 44.0)

崩溃的原因是UIViewUIView?是完全不同的类型。 UIViewObjective-C class,而 UIView?Swift 枚举 其中可以包含 UIView。相反,在 Objective-C 中 nullable 只是对编译器的提示。