Подтвердить что ты не робот

Как развернуть необязательное значение из любого типа?

Учитывая массив [Any], который содержит сочетание необязательных и необязательных значений, например:

let int:Int? = 1
let str:String? = "foo"

let values:[Any] = [int,2,str,"bar"]

Как мы можем извлечь значение Optional в типе Any (если оно есть), чтобы мы могли создать общую функцию печати, которая выводит только значения.

например. эта функция 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

Но хотелось бы посмотреть, как мы можем развернуть опцию из Any, так как это возвращает MirrorType.value, что затрудняет извлечение необязательного значения, например:

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
4b9b3361

Ответ 1

Для 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? всегда будет распаковывать любой тип.

Ответ 2

Я думаю, что это своего рода ошибка.

В общем случае, чтобы обнаружить и извлечь конкретный тип из Any, downing с 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]))")
    }
}

Ответ 3

Чтобы проверить, является ли переменная Any опциональной протокол может использоваться как средство необязательного Необязательного.

Так же, как его в настоящее время невозможно (как и для Swift 2), чтобы проверить на отсутствие Необязательный Необязательно, также невозможно включить a в необязательный вариант:

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 также может использоваться для предоставления универсального интерфейса для доступа к необязательным значениям и даже разворачивания их:

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

Ответ 4

Чтобы, возможно, спасти кого-то от вымотания всего этого из ответов и комментариев, вот ответ, включающий как "нормальные" способы, так и некоторые, что я считаю улучшением для Swift 3, идущего с Xcode 8.2.1.

Использование 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. Как говорится в его комментариях, изменение .Optional до .Optional исправляет это (см. SE-0005 и Руководство по разработке Swift API).

Причины, по которым я думал, что это решение можно улучшить:

  • Я нахожу возвращение NSNull() странным.
  • Я думаю, что альтернатива возврата nil с типом возврата Any? также проблематична, потому что она превращает все (включая необязательные значения) в необязательные значения (например, unwrap(any: 42) возвращает Optional(42)).
  • При вызове unwrap(any:) с чем-либо, кроме значения Any (более того, кто-нибудь?) компилятор Swift 3 предупреждает о неявно принуждение к Any.

Символьные мысли касаются ответа Сайджона.

Решение, которое я предлагаю, адресует все эти точки. Имейте в виду, однако, что unwrap(_:) возвращает 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()
}

Обсуждение

Это базовое решение 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))")
}

Независимо от того, используете ли вы unwrap() или unwrapUsingProtocol(), это напечатает

id = 1
name = foo

Если вы ищете способ аккуратного выравнивания вывода, см. Есть ли способ использовать вкладки для равномерного выделения строк описания в Swift?

Ответ 5

Не полный ответ. Это сводится к следующему:

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 optional: \(v)") // here I'm stuck
    }else {
      println("value[\(i)] = \(values[i])")
    }
  }
}

printArray(values)

Ответ 6

Как насчет этого решения, я сделал общую версию предыдущего ответа.

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)

Ниже приведен код на игровой площадке.

введите описание изображения здесь

Ответ 7

Незначительное изменение на @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)
}

Ответ 8

Не делая этого слишком сложным, почему бы и нет:

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
бар

Ответ 9

Основываясь на решении @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?)

Ответ 10

В соответствии с использованием шаблонов событий перечисления в Swift 2.0 они могут выглядеть следующим образом:

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

Вывод: 332, 232, 55