在可选项中包装swift变量的简写?

时间:2015-04-20 21:42:02

标签: swift variables syntax casting optional

Swift允许我们使用简写符号str!来解包一个可选项。但是,如果我们想做相反的事情呢?

说我有一个变量:

var str = String() // String

是否有任何简写符号将其转换为可选项(即String?String!)?

(例如,我想做var strOptional = ?(str)之类的事情。)

或者,如果此表示法没有简写,如何在不明确提及其类型的情况下将其转换为可选项(例如,我不想提及String)。

换句话说,我知道我可以使用以下任何方法将变量包装为可选:

var strOptional = str as String?
var strOptional: String? = str
var strOptional = String?(str)

...但在每种情况下,我都必须明确写出String

如果没有简写语法,我宁愿写一些像var strOptional = str as typeof?(str)这样的东西。 (优点是如果变量的类型经常在代码库中更改,那么更新的位置就会少一些。)


至于这个有用的实际示例,想象一下我想使用AVCaptureDevice并使用以下代码:

let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
device.lockForConfiguration(nil)

lockForConfiguration()将在没有摄像机的设备上运行时崩溃,编译器不会向我发出警告。原因是defaultDeviceWithMediaType可能会根据文档 [1] 返回nil,但它被定义为返回AVCaptureDevice!

要修复这样的错误API,最好这样做:

let device = ?(AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo))

...得到一个AVCaptureDevice?,并让编译器发现我可能犯的任何错误。

目前,我必须采用更详细的说法:

let device: AVCaptureDevice? = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)

另一个例子:

在这种情况下,我想为我的变量提供一个默认值,这是一个字符串,但是稍后,我可能希望为它分配一个nil值。

var myString = "Hi"
// ...
if (someCondition()) {
    myString = nil // Syntax error: myString is String
}

目前我不得不诉诸var myString: String? = "Hi",但var myString = ?("Hi")之类的内容会更简洁。


[1]如果打开AVCaptureDevice.h,您将看到有关返回值的以下文档:“具有给定媒体类型的默认设备,如果不存在具有该媒体类型的设备,则为nil。”< /子>

3 个答案:

答案 0 :(得分:2)

可选只是swift中的枚举,因此您可以执行:Optional(str)

来自swift界面:

/// A type that can represent either a `Wrapped` value or `nil`, the absence
/// of a value.
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
    case None
    case Some(Wrapped)
    /// Construct a `nil` instance.
    public init()
    /// Construct a non-`nil` instance that stores `some`.
    public init(_ some: Wrapped)
    /// If `self == nil`, returns `nil`.  Otherwise, returns `f(self!)`.
    @warn_unused_result
    public func map<U>(@noescape f: (Wrapped) throws -> U) rethrows -> U?
    /// Returns `nil` if `self` is `nil`, `f(self!)` otherwise.
    @warn_unused_result
    public func flatMap<U>(@noescape f: (Wrapped) throws -> U?) rethrows -> U?
    /// Create an instance initialized with `nil`.
    public init(nilLiteral: ())
}

答案 1 :(得分:0)

要包装变量,您可以使用运算符重载和泛型函数。例如:

prefix operator ??? {}

prefix func ??? <T> (x: T) -> T? {
    return Optional(x)
}

要将此与您的第二个示例相关联,您将执行以下操作:

var myString = ???"Hi" // Optional("Hi")

if someCondition() {
    myString = nil
}

答案 2 :(得分:0)

在轻松的精神中,据我所知,提出的问题是,您可以定义一个后缀运算符,它使用一个小于理想解决方案的字符(一对parens和一个问号):

postfix operator =? {}
postfix func =? <T> (rhs: T) -> T? { return rhs }
postfix func =? <T> (rhs: T!) -> T? { return rhs ?? nil }

var x = 3=?
x is Int? //--> true

var imp: Int! = 3
var opt = imp=?
opt is Int? //--> true

至于你提到的Apple的API,因为他们已经返回了一个可选的,虽然是一个隐式解包的可选项,你可以使用nil合并运算符??将它转换为普通的可选:

let y: Int! = 3 // posing for a rogue API 

y is Int? //--> false

let z = y ?? nil

z is Int? //--> true

对于一个狡猾的运算符,一个更有趣的使用方法可能是一个更高阶的运算符函数,它将T -> U提升为T? -> U? ...

func opt <T, U> (f: T -> U) -> T? -> U? { // we'll implement this as an operator
    return { $0.map(f) }
}

postfix operator ->? {}
postfix func ->? <T, U> (f: T -> U) -> T? -> U? {
    return { $0.map(f) }
}

现在,让我们使用它:

let square: Int -> Int = { $0 * $0 }

let i: Int? = 3

square->? (i)

相当于:

opt(square)(i)

这相当于(但可能比这更通用):

i.map(square)

与管道前移操作员一起使用时,这一切都更有意义:

infix operator |> { associativity left precedence 89 }
func |> <A, B>(a: A, f: A -> B) -> B {
    return f(a)
}

然后你可以这样做:

i |> opt(square)

i |> square->?

或者我们可以将它们混合到一个可选的管道转发中,它也可以处理隐式解包的选项:

infix operator ?> { associativity left precedence 89 }
func ?> <T, U> (lhs: T?, rhs: T -> U) -> U? {
    return lhs.map(rhs)
}

let im: Int! = 5
let op: Int? = 5

im ?> square |> debugPrintln //--> Optional(25)
op ?> square |> debugPrintln //--> Optional(25)

相当于:

debugPrintln(op.map(square))