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.window
是 UIWindow??
。
有人知道为什么吗?
属性的声明是
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 你的 realAppDelegate
的 window
属性… 只有 realAppDelegate
决定声明 属性 本身(不需要这样做,因为它是 optional var
)。
- 如果
realAppDelegate
本身没有实现 window
,您可能打算 return 一个 nil
UIWindow?
作为结果。
- 如果您的
realAppDelegate
确实实现了 window
属性,那么您需要 return 按原样进行(如果此实现 return 是实际 UIWindow
或 nil
个)。
最简单的方法是在 Swift 中使用零合并运算符 ??
。 a ?? b
表示 "if a is non-nil, then return a, but if a is nil, return b"(如果 a
是 T?
类型,那么整个表达式应该 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
}
}
}
...
}
对于我的一个项目,我必须创建一个代理 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.window
是 UIWindow??
。
有人知道为什么吗?
属性的声明是
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 你的 realAppDelegate
的 window
属性… 只有 realAppDelegate
决定声明 属性 本身(不需要这样做,因为它是 optional var
)。
- 如果
realAppDelegate
本身没有实现window
,您可能打算 return 一个nil
UIWindow?
作为结果。 - 如果您的
realAppDelegate
确实实现了window
属性,那么您需要 return 按原样进行(如果此实现 return 是实际UIWindow
或nil
个)。
最简单的方法是在 Swift 中使用零合并运算符 ??
。 a ?? b
表示 "if a is non-nil, then return a, but if a is nil, return b"(如果 a
是 T?
类型,那么整个表达式应该 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
}
}
}
...
}