如何从任何types打开可选的值?

给定一个包含可选和非可选值的[Any]数组,例如:

 let int:Int? = 1 let str:String? = "foo" let values:[Any] = [int,2,str,"bar"] 

我们怎样才能提取Anytypes的Optional值(如果有的话),所以我们可以创build一个通用的打印函数,只打印出值。

例如,这个printArray函数经过并打印每个元素:

 func printArray(values:[Any]) { for i in 0..<values.count { println("value[\(i)] = \(values[i])") } } printArray(values) 

哪个会输出:

 value[0] = Optional(1) value[1] = 2 value[2] = Optional("foo") value[3] = bar 

我们怎样才能改变它,所以它只打印底层的价值,以便它打开的价值,如果它是可选的? 例如:

 value[0] = 1 value[1] = 2 value[2] = foo value[3] = bar 

更新进度…

将参数更改为[Any?]时可以工作,例如:

 let values:[Any?] = [int,2,str,"bar"] func printArray(values:[Any?]) { for i in 0..<values.count { println("value[\(i)] = \(values[i]!)") } } printArray(values) 

其中将打印所需的:

 value[0] = 1 value[1] = 2 value[2] = foo value[3] = bar 

但是仍然希望看到我们如何解开Optional from Any因为这是MirrorType.value返回的结果,使得提取Optional值变得困难,例如:

 class Person { var id:Int = 1 var name:String? } var person = Person() person.name = "foo" var mt:MirrorType = reflect(person) for i in 0 ..< mt.count { let (name, pt) = mt[i] println("\(name) = \(pt.value)") } 

打印出来:

 id = 1 name = Optional("foo") 

当我需要:

 id = 1 name = foo 

对于Xcode 7和Swift 2:

 func unwrap(any:Any) -> Any { let mi = Mirror(reflecting: any) if mi.displayStyle != .Optional { return any } if mi.children.count == 0 { return NSNull() } let (_, some) = mi.children.first! return some } let int:Int? = 1 let str:String? = "foo" let null:Any? = nil let values:[Any] = [unwrap(int),2,unwrap(str),"bar", unwrap(null)] 

这会给你[1, 2, "foo", "bar", {NSObject}]

NSNull()更改为nil并将解包func的返回值更改为Any? 将永远解开任何types。

我认为这是一种错误。

一般来说,为了从Any发现和提取特定types,使用as唯一支持的方法向下投射。 但是:

 let int:Int? = 1 let any:Any = int switch any { case let val as Optional<Int>: // < [!] cannot downcast from 'Any' to a more optional type 'Optional<Int>' print(val) default: break } 

这意味着,没有支持的方式来做到这一点。

无论如何,显然你可以做到这一点reflect

 func printArray(values:[Any]) { for i in 0..<values.count { var val = values[i] var ref = reflect(val) // while `val` is Optional and has `Some` value while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" { // replace `val` with unwrapped value val = ref[0].1.value; ref = reflect(val) } println("value[\(i)] = \(val)") } } let int:Int? = 1 let str:String? = "foo" let values:[Any] = [int,2,str,"bar"] printArray(values) 

输出:

 value[0] = 1 value[1] = 2 value[2] = foo value[3] = bar 

增加:小调整版本

 func printArray(values:[Any]) { for i in 0..<values.count { var ref = reflect(values[i]) // while `val` is Optional and has `Some` value while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" { // Drill down to the Mirror of unwrapped value ref = ref[0].1 } let val = ref.value println("value[\(i)] = \(val)") } } 

分解成一个函数:

 func unwrapAny(val:Any) -> Any { var ref = reflect(val) while ref.disposition == .Optional && ref.count > 0 && ref[0].0 == "Some" { ref = ref[0].1 } return ref.value } func printArray(values:[Any]) { for i in 0..<values.count { println("value[\(i)] = \(unwrapAny(values[i]))") } } 

要检查一个Anyvariables是否是可选的,可以使用一个协议作为无types的可选方法 。

就像它目前不可变的(从Swift 2开始)检查一个无types的可选项,也不可能将其转换为无types的可选项:

 let anyType: Any.Type = Optional<String>.self let anyThing: Any = Optional.Some("string") anyType is Optional.Type // Causes error let maybeString = anything as? Optional // Also causes error // Argument for generic parameter 'Wrapped' could not be inferred 

但是,提议的OptionalProtocol也可以用来提供一个通用的接口来访问Optional值,甚至可以解开它们:

 protocol OptionalProtocol { func isSome() -> Bool func unwrap() -> Any } extension Optional : OptionalProtocol { func isSome() -> Bool { switch self { case .None: return false case .Some: return true } } func unwrap() -> Any { switch self { // If a nil is unwrapped it will crash! case .None: preconditionFailure("nill unwrap") case .Some(let unwrapped): return unwrapped } } } // With this we can check if we have an optional let maybeString: String? = "maybe" let justString: String = "just" maybeString is OptionalProtocol // true justString is OptionalProtocol // false 

通过提供的方法,可以非常自然的方式检查和访问可选项,而不需要不可能的转换为可Optional

 let values:[Any] = [ Optional.Some(12), 2, Optional<String>.None, // a "wrapped" nil for completeness Optional.Some("maybe"), "something" ] for any in values { if let optional = any as? OptionalProtocol { if optional.isSome() { print(optional.unwrap()) } else { // nil should not be unwrapped! print(optional) } continue } print(any) } 

哪个会打印:

 12 2 nil maybe something 

为了避免别人从答案和评论中拼凑出来,下面是一个答案,包括“理智”的方式和一些我认为是对Xcode 8.2.1的Swift 3的改进。

使用reflection

 func unwrap<T>(_ any: T) -> Any { let mirror = Mirror(reflecting: any) guard mirror.displayStyle == .optional, let first = mirror.children.first else { return any } return first.value } 

讨论

从bubuxu接受的答案不能用Swift 3进行编译。随着walkline在他的评论中提出,改变.optional to .optional修复了这个问题(参见SE-0005和Swift APIdevise指南 )。

理由我认为这个解决scheme可以改进:

  • 我发现返回NSNull()怪异。
  • 我认为用返回typesAny?返回nil的替代方法Any? 也是有问题的,因为它将一切(包括非可选值)变成可选值(例如unwrap(any: 42)返回Optional(42) )。
  • 当用除Any值之外的Any值(任何其他任何值)调用unwrap(any:) ,Swift 3编译器会警告隐式强制为Any

对于Sajjon的回答 ,也有类似的想法。

我build议的解决scheme解决了所有这些问题。 但是请注意, unwrap(_:)返回nil types为Any因此使用nil合并运算符不再有效。 这意味着这只是转移我认为对第二点有问题的事情。 但是我发现这对于(对我来说)更为有趣的reflection用例来说是正确的。

在可选的情况下使用扩展

 protocol OptionalProtocol { func isSome() -> Bool func unwrap() -> Any } extension Optional : OptionalProtocol { func isSome() -> Bool { switch self { case .none: return false case .some: return true } } func unwrap() -> Any { switch self { case .none: preconditionFailure("trying to unwrap nil") case .some(let unwrapped): return unwrapped } } } func unwrapUsingProtocol<T>(_ any: T) -> Any { guard let optional = any as? OptionalProtocol, optional.isSome() else { return any } return optional.unwrap() } 

讨论

这是基本LopSae的解决scheme更新到Swift 3.我也改变了先决条件失败的消息,并添加unwrapUsingProtocol(_:)

用法

 class Person { var id:Int = 1 var name:String? } var person = Person() person.name = "foo" let mirror = Mirror(reflecting: person) for child in mirror.children.filter({ $0.label != nil }) { print("\(child.label!) = \(unwrap(child.value))") } 

不pipe你使用unwrapUsingProtocol()还是unwrapUsingProtocol() ,都会打印

 id = 1 name = foo 

如果你正在寻找一种整齐排列输出的方法,请看有没有一种方法可以使用制表符在Swift中均匀分隔描述string?

不完整的答案。 这归结于:

 let int:Int? = 1 let str:String? = "foo" let values:[Any] = [int,2,str,"bar"] func printArray(values:[Any]) { for i in 0..<values.count { let v = values[i] if _stdlib_demangleName(_stdlib_getTypeName(v)) == "Swift.Optional" { println("value[\(i)] = "it's optional: \(v)") // here I'm stuck }else { println("value[\(i)] = \(values[i])") } } } printArray(values) 

如何解决这个问题,我提出了以前答案的通用版本。

 fileprivate func unwrap<T>(value: Any) -> (unwraped:T?, isOriginalType:Bool) { let mirror = Mirror(reflecting: value) let isOrgType = mirror.subjectType == Optional<T>.self if mirror.displayStyle != .optional { return (value as? T, isOrgType) } guard let firstChild = mirror.children.first else { return (nil, isOrgType) } return (firstChild.value as? T, isOrgType) } let value: [Int]? = [0] let value2: [Int]? = nil let anyValue: Any = value let anyValue2: Any = value2 let unwrappedResult:([Int]?, Bool) = unwrap(value: anyValue) // ({[0]}, .1 true) let unwrappedResult2:([Int]?, Bool) = unwrap(value: anyValue2) // (nil, .1 true) let unwrappedResult3:([UInt]?, Bool) = unwrap(value: anyValue) // (nil, .1 false) let unwrappedResult4:([NSNumber]?, Bool) = unwrap(value: anyValue) ({[0]}, .1 false) 

以下是Playground上的代码。

在这里输入图像描述

没有太复杂,为什么不呢:

 let int:Int? = 1 let str:String? = "foo" let values:[Any?] = [int,2,str,"bar"] for var i:Int = 0; i < values.count; i++ { println("\(values[i]!)") } 

这打印:

1
2
FOO
酒吧

基于@bubuxu的解决scheme,还可以:

 func unwrap<T: Any>(any: T) -> T? { let mirror = Mirror(reflecting: any) guard mirror.displayStyle == .optional else { return any } guard let child = mirror.children.first else { return nil } return unwrap(any: child.value) as? T } 

但是你需要检查零利用?? nil 当使用unwrap ,如在foo所做的那样

 func foo<T>(_ maybeValue: T?) { if let value: T = unwrap(any: maybeValue) ?? nil { print(value) } } 

尽pipe如此!

任何人都得到了一个解决scheme??? ?? nil检查?

对@thm进行轻微的修改以完全解开:

 func unwrap<T>(_ any: T) -> Any { let mirror = Mirror(reflecting: any) guard mirror.displayStyle == .optional, let first = mirror.children.first else { return any } return unwrap(first.value) } 

根据在Swift 2.0中使用枚举案例模式 ,可能看起来像这样:

 let pattern :[Int?] = [nil, 332, 232,nil,55] for case let number? in pattern { print(number) } 

输出:332,232,55