我正在尝试用Swift重写ObjC代码的部分内容,许多Cocoa对象属性在Swift中返回可选值。是否有更好的模式替代这种深层嵌套:
if let panel = self.window {
if let screens = NSScreen.screens() {
if let screenRect = screens[0].frame {
let statusRect = CGRectZero
...
我正在尝试用Swift重写ObjC代码的部分内容,许多Cocoa对象属性在Swift中返回可选值。是否有更好的模式替代这种深层嵌套:
if let panel = self.window {
if let screens = NSScreen.screens() {
if let screenRect = screens[0].frame {
let statusRect = CGRectZero
...
虽然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绑定的启发。但尽管有趣,我不建议这样做。我认为这很难懂。(顺便说一下,???
和??
之间的区别是前者不需要lhs和rhs是相同类型(或更高类型),而后者则需要。 ???
始终返回其rhs或nil
。)
?.
和 if let
的组合。 - Gregory Higley一段时间前,我写了一个博客文章,探讨了几种不同的方法来解包多个可选项。
您可以创建一个函数,在一个步骤中解包多个可选项:
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)")
}
有人也提出了一个使用元组的简洁解决方案,可以在这个代码片段中找到。
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)
}
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
}