给定一个包含[Any]
可选值和非可选值的数组,例如:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
Run Code Online (Sandbox Code Playgroud)
我们怎样才能提取的值Optional
的Any
类型(如果有的话),所以我们可以创建一个通用的打印功能,仅打印出的值.
例如,这个printArray函数遍历并打印每个元素:
func printArray(values:[Any]) {
for i in 0..<values.count {
println("value[\(i)] = \(values[i])")
}
}
printArray(values)
Run Code Online (Sandbox Code Playgroud)
哪个会输出:
value[0] = Optional(1)
value[1] = 2
value[2] = Optional("foo")
value[3] = bar
Run Code Online (Sandbox Code Playgroud)
我们如何更改它以便它只打印基础值,以便在可选时将其展开值?例如:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
Run Code Online (Sandbox Code Playgroud)
更改参数时它可以工作[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)
Run Code Online (Sandbox Code Playgroud)
这将打印所需的:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
Run Code Online (Sandbox Code Playgroud)
但是仍然希望看到我们如何解包一个Optional,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)")
}
Run Code Online (Sandbox Code Playgroud)
打印出来:
id = 1
name = Optional("foo")
Run Code Online (Sandbox Code Playgroud)
当我需要时:
id = 1
name = foo
Run Code Online (Sandbox Code Playgroud)
bub*_*uxu 31
对于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)]
Run Code Online (Sandbox Code Playgroud)
这会给你 [1, 2, "foo", "bar", {NSObject}]
更改NSNull()
为nil
unwrap func的返回值Any?
将始终解包任何类型.
thm*_*thm 13
为了节省一些人从答案和评论中拼凑起来,这里有一个答案包括"理智"的方式和一些我认为是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
}
Run Code Online (Sandbox Code Playgroud)
bubuxu接受的答案无法使用Swift 3进行编译.正如walkline在他的评论中所说,.Optional
改为.optional
修复此问题(参见SE-0005和Swift API设计指南).
我认为这个解决方案可以改进的原因:
NSNull()
怪异.nil
返回类型返回的替代Any?
方法也存在问题,因为它将所有内容(包括非可选值)转换为可选值(例如unwrap(any: 42)
返回Optional(42)
).unwrap(any:)
用除了Any
值之外的任何东西调用(任何更多任何人?)时,Swift 3编译器警告隐式强制转换Any
.我建议解决所有这些问题的解决方案.但请注意,unwrap(_:)
返回nil
类型,Any
因此使用nil coalescing运算符不再起作用.这意味着这只是围绕我认为关于第二点的问题而改变.但我发现这对于(对我来说)更有趣的反思用例来说是正确的做法.
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()
}
Run Code Online (Sandbox Code Playgroud)
这是基本上LopSae的解决方案更新到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))")
}
Run Code Online (Sandbox Code Playgroud)
无论你是否使用unwrap()
或unwrapUsingProtocol()
,都会打印出来
id = 1
name = foo
Run Code Online (Sandbox Code Playgroud)
如果您正在寻找一种整齐排列输出的方法,请参阅是否有办法使用制表符在Swift中均匀分隔描述字符串?
Lop*_*Sae 10
要检查Any
变量是否是可选的,协议可以用作无类型可选的方法.
正如它目前不可能(如Swift 2)检查无类型的 Optional一样,也无法将其转换为无类型的可选:
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
Run Code Online (Sandbox Code Playgroud)
但是,建议的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
Run Code Online (Sandbox Code Playgroud)
使用提供的方法,可以以非常自然的方式检查和访问选项,而无需不必要的强制转换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)
}
Run Code Online (Sandbox Code Playgroud)
哪个会打印:
12
2
nil
maybe
something
Run Code Online (Sandbox Code Playgroud)
我认为这是一种bug.
通常,要发现和提取特定类型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
}
Run Code Online (Sandbox Code Playgroud)
这意味着,没有支持的方法来做到这一点.
无论如何,显然你可以做到这一点 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)
Run Code Online (Sandbox Code Playgroud)
输出:
value[0] = 1
value[1] = 2
value[2] = foo
value[3] = bar
Run Code Online (Sandbox Code Playgroud)
增加:小调整版
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)")
}
}
Run Code Online (Sandbox Code Playgroud)
考虑到功能:
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]))")
}
}
Run Code Online (Sandbox Code Playgroud)
在@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)
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
13944 次 |
最近记录: |