展开多个 Swift 选项
Unwrapping multiple Swift optionals
我正在尝试用 Swift 重写部分 ObjC 代码,Swift 中有很多 cocoa 对象属性 return 可选值。有没有更好的模式而不是这种深度嵌套:
if let panel = self.window {
if let screens = NSScreen.screens() {
if let screenRect = screens[0].frame {
let statusRect = CGRectZero
...
你可以像这样强制解包:
NSScreen.screens()!.first!.frame!
NSScreen.screens()!.first!.frame!.width
NSScreen.screens()!.first!.frame!.height
// safely unwraping it as required an mentioned by Gregory
if let frame = NSScreen.screens()?.first?.frame {
println(frame)
}
if let width = NSScreen.screens()?.first?.frame?.width {
println(width)
}
if let height = NSScreen.screens()?.first?.frame?.height {
println(height)
}
虽然 Leonardo 的回答很好,但它可能会导致异常,除非你知道对象是非可选的,更好的模式是这样的,它应该被视为伪代码:
if let frame = NSScreen.screens()?.first?.frame {
// Do something with frame.
}
换句话说,使用可选链接 (?.
),但仅在链的最后部分使用 let
。
如果你想链接不同类型的可选值,你也可以创建这样的运算符,其中只返回最后一个的值:
infix operator ??? { associativity left }
func ??? <T1, T2>(t1: T1?, t2: @autoclosure () -> T2?) -> T2? {
return t1 == nil ? nil : t2()
}
if let frame = self.window ??? NSScreen.screens()?.first?.frame {
// Do something with frame
}
这当然是受到了Haskell的bind的启发。但尽管这很有趣,我不推荐它。我认为它很清楚。 (顺便说一下,???
和 ??
之间的区别是前者 不 要求 lhs 和 rhs 是相同的类型(或超类型) ,而后者确实如此。???
总是 returns 它的 rhs 或 nil
。)
我 wrote a blog post 不久前探索了几种不同的方法来解包多个可选值。
您可以创建一个一次性解包多个可选值的函数:
func if_let<T, U, V>(a: Optional<T>, b: Optional<U>,
c: Optional<V>, fn: (T, U, V) -> ()) {
if let a = a {
if let b = b {
if let c = c {
fn(a, b, c)
}
}
}
}
用法如下:
if_let(a, b, c) {
a, b, c in
println("\(a) - \(b) - \(c)")
}
有人还提出了一个使用元组的巧妙解决方案,可以是 found in this gist。
从 Swift 1.2 开始,您可以在一行中展开,如以下答案所述:
unwrapping multiple optionals in if statement
请在此处阅读有关可选链接的信息:
Linking Multiple Levels of Chaining
你可以玩这个游乐场看看它是如何工作的:
import UIKit
class AAA {
var string: String? = nil
}
class AA {
var aaa: [AAA?]? = nil
}
class A {
var aa: AA? = nil
}
var a: A?
a = A()
/// Comment/Uncomment next lines
a?.aa = AA()
//a?.aa?.aaa = [AAA()]
a?.aa?.aaa = []
a?.aa?.aaa?.first??.string = "My String Value"
if let myString = a?.aa?.aaa?.first??.string {
print("myString: \(myString)")
} else {
print("myString does not acceptable")
}
您的代码可能如下所示:
if let panel = self.window, let statusRect = NSScreen.screens()?.first??.frame ?? CGRect.zero {
/// your code to work with panel and statusRect
}
我正在尝试用 Swift 重写部分 ObjC 代码,Swift 中有很多 cocoa 对象属性 return 可选值。有没有更好的模式而不是这种深度嵌套:
if let panel = self.window {
if let screens = NSScreen.screens() {
if let screenRect = screens[0].frame {
let statusRect = CGRectZero
...
你可以像这样强制解包:
NSScreen.screens()!.first!.frame!
NSScreen.screens()!.first!.frame!.width
NSScreen.screens()!.first!.frame!.height
// safely unwraping it as required an mentioned by Gregory
if let frame = NSScreen.screens()?.first?.frame {
println(frame)
}
if let width = NSScreen.screens()?.first?.frame?.width {
println(width)
}
if let height = NSScreen.screens()?.first?.frame?.height {
println(height)
}
虽然 Leonardo 的回答很好,但它可能会导致异常,除非你知道对象是非可选的,更好的模式是这样的,它应该被视为伪代码:
if let frame = NSScreen.screens()?.first?.frame {
// Do something with frame.
}
换句话说,使用可选链接 (?.
),但仅在链的最后部分使用 let
。
如果你想链接不同类型的可选值,你也可以创建这样的运算符,其中只返回最后一个的值:
infix operator ??? { associativity left }
func ??? <T1, T2>(t1: T1?, t2: @autoclosure () -> T2?) -> T2? {
return t1 == nil ? nil : t2()
}
if let frame = self.window ??? NSScreen.screens()?.first?.frame {
// Do something with frame
}
这当然是受到了Haskell的bind的启发。但尽管这很有趣,我不推荐它。我认为它很清楚。 (顺便说一下,???
和 ??
之间的区别是前者 不 要求 lhs 和 rhs 是相同的类型(或超类型) ,而后者确实如此。???
总是 returns 它的 rhs 或 nil
。)
我 wrote a blog post 不久前探索了几种不同的方法来解包多个可选值。
您可以创建一个一次性解包多个可选值的函数:
func if_let<T, U, V>(a: Optional<T>, b: Optional<U>,
c: Optional<V>, fn: (T, U, V) -> ()) {
if let a = a {
if let b = b {
if let c = c {
fn(a, b, c)
}
}
}
}
用法如下:
if_let(a, b, c) {
a, b, c in
println("\(a) - \(b) - \(c)")
}
有人还提出了一个使用元组的巧妙解决方案,可以是 found in this gist。
从 Swift 1.2 开始,您可以在一行中展开,如以下答案所述:
unwrapping multiple optionals in if statement
请在此处阅读有关可选链接的信息: Linking Multiple Levels of Chaining
你可以玩这个游乐场看看它是如何工作的:
import UIKit
class AAA {
var string: String? = nil
}
class AA {
var aaa: [AAA?]? = nil
}
class A {
var aa: AA? = nil
}
var a: A?
a = A()
/// Comment/Uncomment next lines
a?.aa = AA()
//a?.aa?.aaa = [AAA()]
a?.aa?.aaa = []
a?.aa?.aaa?.first??.string = "My String Value"
if let myString = a?.aa?.aaa?.first??.string {
print("myString: \(myString)")
} else {
print("myString does not acceptable")
}
您的代码可能如下所示:
if let panel = self.window, let statusRect = NSScreen.screens()?.first??.frame ?? CGRect.zero {
/// your code to work with panel and statusRect
}