如何从 Any 类型中解包一个可选值?
How to unwrap an optional value from Any type?
给定一个包含可选值和非可选值的 [Any]
数组,例如:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
我们如何提取 Any
类型(如果有)中的 Optional
的值,以便我们可以创建一个仅打印出值的通用打印函数。
例如这个 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
我们如何更改它,使其仅打印基础值,以便在它是 Optional 时解包该值?例如:
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
但仍然想看看我们如何从 Any
中解包 Optional,因为这就是 MirrorType.value
returns 使得提取 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
不是一个完整的答案。归结为:
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)
在不让它变得太复杂的情况下,为什么不呢:
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
富
酒吧
我认为这是一种错误。
一般来说,要从 Any
中发现并提取特定类型,使用 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]))")
}
}
对于 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
并将 unwrap func 的 return 值更改为 Any?
将始终解包任何类型。
检查 Any
变量是否是可选的 。
正如目前不可能(从 Swift 2 开始)检查 无类型 可选一样,也不能将 an 转换为 无类型可选:
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
根据在 Swift 2.0 中使用 枚举案例模式
这些可能看起来像这样:
let pattern :[Int?] = [nil, 332, 232,nil,55]
for case let number? in pattern {
print(number)
}
输出:
332、
232、
55
根据@bubuxu的解决方案,还可以:
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
}
但是您需要在使用 unwrap
时使用 ?? nil
检查 nil,就像在 foo
中所做的那样
func foo<T>(_ maybeValue: T?) {
if let value: T = unwrap(any: maybeValue) ?? nil {
print(value)
}
}
不过还是很整洁!
(有人找到 ?? nil
检查的解决方案吗?)
这个解决方案怎么样,我做了一个以前答案的通用版本。
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上的代码。
为了避免有人从答案和评论中拼凑出来,这里有一个答案,包括 "sane" 方式和一些我认为是 Swift 3 的改进 Xcode 8.2.1.
使用反射
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
}
讨论
接受的 无法通过 Swift 3 编译。
正如 walkline 在他的评论中建议的那样,将 .Optional
更改为 .optional
可以解决此问题(请参阅 SE-0005 and Swift API Design Guidelines)。
我认为这个解决方案可以改进的原因:
- 我觉得 returning
NSNull()
很奇怪。
- 我认为 returning
nil
与 return 类型 Any?
的替代方案也是有问题的,因为它将所有内容(包括非可选值)转换为可选值
(例如 unwrap(any: 42)
returns Optional(42)
)。
- 当使用
Any
值以外的任何值调用 unwrap(any:)
时(还有任何人吗?) Swift 3 编译器会隐式警告
强制 Any
.
类似的想法适用于 。
我建议的解决方案解决了所有这些问题。但是请注意 unwrap(_:)
returns nil
作为类型 Any
所以使用 nil
合并运算符不再起作用。这意味着这只是围绕第二点我认为有问题的地方转移。但我发现这对于(对我而言)关于反射的更有趣的用例来说是正确的做法。
在可选上使用扩展
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()
}
讨论
这基本上更新为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({ [=12=].label != nil }) {
print("\(child.label!) = \(unwrap(child.value))")
}
无论您使用 unwrap()
还是 unwrapUsingProtocol()
,都会打印
id = 1
name = foo
如果您正在寻找一种整齐对齐输出的方法,请参阅
对@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)
}
给定一个包含可选值和非可选值的 [Any]
数组,例如:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
我们如何提取 Any
类型(如果有)中的 Optional
的值,以便我们可以创建一个仅打印出值的通用打印函数。
例如这个 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
我们如何更改它,使其仅打印基础值,以便在它是 Optional 时解包该值?例如:
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
但仍然想看看我们如何从 Any
中解包 Optional,因为这就是 MirrorType.value
returns 使得提取 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
不是一个完整的答案。归结为:
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)
在不让它变得太复杂的情况下,为什么不呢:
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
富
酒吧
我认为这是一种错误。
一般来说,要从 Any
中发现并提取特定类型,使用 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]))")
}
}
对于 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
并将 unwrap func 的 return 值更改为 Any?
将始终解包任何类型。
检查 Any
变量是否是可选的
正如目前不可能(从 Swift 2 开始)检查 无类型 可选一样,也不能将 an 转换为 无类型可选:
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
根据在 Swift 2.0 中使用 枚举案例模式 这些可能看起来像这样:
let pattern :[Int?] = [nil, 332, 232,nil,55]
for case let number? in pattern {
print(number)
}
输出: 332、 232、 55
根据@bubuxu的解决方案,还可以:
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
}
但是您需要在使用 unwrap
时使用 ?? nil
检查 nil,就像在 foo
func foo<T>(_ maybeValue: T?) {
if let value: T = unwrap(any: maybeValue) ?? nil {
print(value)
}
}
不过还是很整洁!
(有人找到 ?? nil
检查的解决方案吗?)
这个解决方案怎么样,我做了一个以前答案的通用版本。
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上的代码。
为了避免有人从答案和评论中拼凑出来,这里有一个答案,包括 "sane" 方式和一些我认为是 Swift 3 的改进 Xcode 8.2.1.
使用反射
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
}
讨论
接受的 .Optional
更改为 .optional
可以解决此问题(请参阅 SE-0005 and Swift API Design Guidelines)。
我认为这个解决方案可以改进的原因:
- 我觉得 returning
NSNull()
很奇怪。 - 我认为 returning
nil
与 return 类型Any?
的替代方案也是有问题的,因为它将所有内容(包括非可选值)转换为可选值 (例如unwrap(any: 42)
returnsOptional(42)
)。 - 当使用
Any
值以外的任何值调用unwrap(any:)
时(还有任何人吗?) Swift 3 编译器会隐式警告 强制Any
.
类似的想法适用于
我建议的解决方案解决了所有这些问题。但是请注意 unwrap(_:)
returns nil
作为类型 Any
所以使用 nil
合并运算符不再起作用。这意味着这只是围绕第二点我认为有问题的地方转移。但我发现这对于(对我而言)关于反射的更有趣的用例来说是正确的做法。
在可选上使用扩展
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()
}
讨论
这基本上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({ [=12=].label != nil }) {
print("\(child.label!) = \(unwrap(child.value))")
}
无论您使用 unwrap()
还是 unwrapUsingProtocol()
,都会打印
id = 1
name = foo
如果您正在寻找一种整齐对齐输出的方法,请参阅
对@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)
}