如何从任何types打开可选的值?
给定一个包含可选和非可选值的[Any]
数组,例如:
let int:Int? = 1 let str:String? = "foo" let values:[Any] = [int,2,str,"bar"]
我们怎样才能提取Any
types的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]))") } }
要检查一个Any
variables是否是可选的,可以使用一个协议作为无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()
怪异。 - 我认为用返回types
Any?
返回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