给定一个[Any]
数组,其中包含可选值和非可选值的组合,例如:
let int:Int? = 1
let str:String? = "foo"
let values:[Any] = [int,2,str,"bar"]
我们如何在Optional
类型中提取Any
的值(如果有的话),这样我们就可以创建一个只打印出值的通用打印函数。
E.g。这个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
我们如何更改它以便它只打印基础值,以便在可选时将其解包? e.g:
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
返回的内容很难提取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
答案 0 :(得分: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)]
这将为您提供[1, 2, "foo", "bar", {NSObject}]
将NSNull()
更改为nil
,将解包功能的返回值更改为Any?
将始终打开任何类型。
答案 1 :(得分:10)
为了挽救某人从答案和评论中拼凑起来,这里有一个答案,包括&#34;理智&#34;方法和一些我认为是Swift 3改进的Xcode 8.2.1。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.design.widget.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
android:layout_alignParentBottom="true"
/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_above="@id/navigation"
android:layout_height="match_parent">
<!--Your code for the fragment here.-->
</RelativeLayout>
</RelativeLayout>
接受的answer from bubuxu无法使用Swift 3进行编译。
正如walkline在评论中所建议的那样,将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
会修复此问题(请参阅SE-0005和Swift API Design Guidelines)。
我认为可以改进此解决方案的原因:
.optional
很奇怪。NSNull()
的{{1}}的替代方法也存在问题,因为它会将所有内容(包括非可选值)转换为可选值
(例如nil
返回Any?
)。unwrap(any: 42)
值以外的任何值调用Optional(42)
时(任何人都可以吗?)Swift 3编译器会暗中警告
胁迫unwrap(any:)
。类似的想法适用于Sajjon's answer。
我建议解决所有这些问题的解决方案。请注意,Any
会将Any
作为unwrap(_:)
类型返回,因此使用nil
合并运算符不再起作用。这意味着这只是围绕我认为关于第二点的问题而改变。但我发现这对于(对我而言)更有趣的反思用例来说是正确的做法。
nil
这是基本上LopSae's solution更新到Swift 3.我还更改了前置条件失败消息并添加了Any
。
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({ $0.label != nil }) {
print("\(child.label!) = \(unwrap(child.value))")
}
,都会打印
unwrap()
如果您正在寻找一种整齐排列输出的方法,请参阅Is there a way to use tabs to evenly space out description strings in Swift?
答案 2 :(得分:8)
检查Any
变量是否为可选a protocol can be used as a means of a typeless Optional。
正如它目前不可能(如Swift 2)检查无类型可选它也不可能将无类型选中:
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
也可用于提供 generic-less 接口以访问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
答案 3 :(得分:6)
我认为这是一种错误。
通常,要从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
ADDED:小调整版
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]))")
}
}
答案 4 :(得分:4)
@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)
}
答案 5 :(得分:1)
不完整的答案。归结为:
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)
答案 6 :(得分:1)
这个解决方案怎么样,我做了以前答案的通用版本。
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上的代码。
答案 7 :(得分:0)
不要太复杂,为什么不:
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
吧
答案 8 :(得分:0)
基于@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
}
但在使用?? nil
时,您需要使用unwrap
检查nil,如foo
func foo<T>(_ maybeValue: T?) {
if let value: T = unwrap(any: maybeValue) ?? nil {
print(value)
}
}
虽然还很整洁!
(任何人都有?? nil
支票的解决方案?)
答案 9 :(得分:-1)
根据Swift 2.0中的使用枚举案例模式 那些可能看起来像这样:
let pattern :[Int?] = [nil, 332, 232,nil,55]
for case let number? in pattern {
print(number)
}
输出: 332, 232, 55