Apple Swift: типовые обобщения

#ios #generics #swift

#iOS #дженерики #быстрый

Вопрос:

Я пишу некоторый Swift-код, где у меня есть массив, содержащий универсальный тип:

 let _data: Array<T> = T[]()
 

Позже в моем коде мне нужно определить тип, хранящийся в массиве. Я попытался использовать метод приведения типов, описанный в документации (хотя он не использовался для дженериков).

 switch self._data {
case let doubleData as Array<Double>:
  // Do something with doubleData
case let floatData as Array<Float>:
  // Do something with floatData
default:
  return nil // If the data type is unknown return nil
}
 

Приведенный выше оператор switch приводит к следующей ошибке при компиляции:

  1. При излучении ИК-функции SIL @_TFC19Adder_Example__Mac6Matrix9transposeUS_7Элемент__fGS0_Q__FT_GSqGS0_Q___ для «транспонирования» в /code.viperscience/Сумматор / src/ Библиотека сумматоров /Matrix.swift:45:3 :0: ошибка: невозможно выполнить команду: ошибка сегментации: 11:0: ошибка: сбой команды интерфейса swift из-за сигнала (используйте -v, чтобы увидеть вызов) Команда / Applications/Xcode6-Beta2.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr / bin / swift не удалась с кодом выхода 254

Кто-нибудь знает, как я могу привести свои общие данные к их фактическому типу, чтобы предпринять конкретные действия?

Комментарии:

1. Попробуйте использовать as? , но я думаю, что в вашем случае это ошибка компилятора … сообщите об этом!

2. Я попробовал необязательный, но он не сработал. Я согласен, что это, вероятно, ошибка компилятора. Это не первый случай, который я видел, связанный с дженериками…

Ответ №1:

В swift as operator — это что-то вроде dynamic_cast C , которое можно использовать для приведения объекта вниз.

Допустим, у вас есть объект a типа A , и вы можете писать let a as B только тогда, когда type B идентичен type A или B является подклассом A .

В вашем случае, по-видимому Array<T> , не всегда может быть приведено к Array<Double> или Array<Float> , поэтому компилятор сообщает об ошибках.

Простое решение — сначала преобразовать AnyObject в, а затем уменьшить до Array<Double> или Array<Float> :

 let anyData: AnyObject = self._data;
switch anyData {
case let doubleData as? Array<Double>: // use as? operator, instead of as,
                                       // to avoid runtime exception
  // Do something with doubleData
case let floatData as? Array<Float>:
  // Do something with floatData
default:
  return nil // If the data type is unknown return nil
 

Комментарии:

1. Спасибо за помощь, но я безрезультатно попробовал ваше решение, хотя ваше объяснение было полезным. Я понимаю, что Array<T> не всегда может быть преобразован в Array<Float> или Array<Double>, но разве не для этого предназначен оператор switch? Это действительно не должно иметь значения… Я думаю, что это ошибка компилятора. Кроме того, использование оператора As? только генерирует другую ошибку. В документации Apple (см. Мою Ссылку Выше) не используется оператор As?, а только оператор As.

Ответ №2:

Предположим, у вас есть массив кнопок:

 let views: [NSView] = [NSButton(), NSButton(), NSButton()]
 

Вы можете использовать эти приведения:

 let viewsAreButtons = views is [NSButton]  // returns true
let buttonsForSure = views as! [NSButton]  // crashes if you are wrong
let buttonsMaybe = views as? [NSButton]    // optionally set
 

Если вы попытаетесь использовать as в случае переключения, как показано ниже, это не сработает. Компилятор (Swift 1.2 Xcode 6.3b1) сообщает: «Шаблон понижения типа [NSButton] не может быть использован».

 switch views {
  case let buttons as [NSButton]:
    println("Buttons")
  default:
    println("something else")
}
 

Назовите это ограничением. Создайте радар с вашим вариантом использования. Команда Swift действительно старается прислушиваться к отзывам. Если вы действительно хотите, чтобы это работало, вы можете определить свой собственный оператор сопоставления с образцом. В этом случае это было бы что-то вроде этого:

 struct ButtonArray { }
let isButtonArray = ButtonArray()

func ~=(pattern: ButtonArray, value: [NSView]) -> Bool {
    return value is [NSButton]
}
 

Тогда это работает:

 switch views {
  case isButtonArray:
      println("Buttons")    // This gets printed.
  default:
     println("something else")
}
 

Попробуйте это на игровой площадке. Надеюсь, это поможет!