Swift 可选的可选的

Swift Optional of Optional

对于我的一个项目,我必须创建一个代理 AppDelegate,它将调用转发到另一个 AppDelegate。

UIApplicationDelegate 有一个 var window: UIWindow?。我的问题是,为什么我不能这样做:

private lazy var realAppDelegate: UIApplicationDelegate = {
    return AppDelegate()
}()

var window: UIWindow? {
    get {
        return realAppDelegate.window
    }
    set {
        realAppDelegate.window = newValue
    }
}

该代码的问题在于 realAppDelegate.windowUIWindow??

有人知道为什么吗?

属性的声明是

optional var window: UIWindow? { get set }

开头的optional意思是说属性根本不用,那是第二个?

UIApplicationDelegate 是一个协议,实现它的 class 不必实现所有内容。

UIApplicationDelegate协议的属性window是这样声明的:

optional var window: UIWindow? { get set }

这意味着它是一个 可选的 属性(在 "the class implementing the UIApplicationDelegate protocol is not requested to implement/have this property" 的意义上,就像 @optional 在 Objective-C), 属性 是可选类型 Optional<UIWindow> (or UIWindow?).

这就是为什么你最后有双可选类型的原因,因为window 属性可能会也可能不会在realDelegate中实现,如果是,它本身就是类型Optional<UIWindow>/UIWindow?.


所以基本上你想要的是 return 你的 realAppDelegatewindow 属性… 只有 realAppDelegate 决定声明 属性 本身(不需要这样做,因为它是 optional var)。

  • 如果 realAppDelegate 本身没有实现 window,您可能打算 return 一个 nil UIWindow? 作为结果。
  • 如果您的 realAppDelegate 确实实现了 window 属性,那么您需要 return 按原样进行(如果此实现 return 是实际 UIWindownil 个)。

最简单的方法是在 Swift 中使用零合并运算符 ??a ?? b 表示 "if a is non-nil, then return a, but if a is nil, return b"(如果 aT? 类型,那么整个表达式应该 return 是 T 类型的对象,在你的情况下 T 是类型 UIWindow?).

var window: UIWindow? {
    get {
        // If realAppDelegate.window (of type UIWindow??) is not implemented
        // then return nil. Otherwise, return its value (of type UIWindow?)
        return realAppDelegate.window ?? nil
        // That code is equivalent (but more concise) to this kind of code:
        //   if let w = realAppDelegate.window { return w } else return nil
    }
    ...
}

实现setter,这是另一个问题。根据 this SO answer,直接访问协议的可选 属性 的 setter 似乎是不可能的。但是你可以想象一个 hack 来解决这个问题,通过声明另一个协议使这个 window 属性 要求成为强制性的,然后尝试在 setter:

中强制转换它
@objc protocol UIApplicationDelegateWithWindow : UIApplicationDelegate {
    var window: UIWindow? { get set }
}

class AppDelegateWrapper : UIApplicationDelegate {
    ...
    var window: UIWindow? {
        get {
            return realAppDelegate.window ?? nil
        }
        set {
            if let realAppDelWithWindow = realAppDelegate as? UIApplicationDelegateWithWindow
            {
                // Cast succeeded, so the 'window' property exists and is now accessible
                realAppDelWithWindow.window = newValue
            }
        }
    }
...
}