Проверка, является ли объект заданным типом в Swift


267

У меня есть массив, который состоит из AnyObject . Я хочу перебрать его и найти все элементы, которые являются экземплярами массива.

Как я могу проверить, является ли объект данного типа в Swift?


Ваш вопрос спрашивает о том, чтобы найти тип данного объекта, но вы приняли ответ, способный только проверить, принадлежит ли объект данному типу. Я предлагаю вам отредактировать свой вопрос именно так, иначе многие читатели будут недовольны ответом, который вы приняли. (Все остальные ответы похожи, поэтому, к счастью, вам не нужно беспокоиться о том, чтобы сделать их недействительными, сузив свой вопрос.)
Джереми Бэнкс

Я отредактировал этот вопрос, чтобы устранить неоднозначность на сайте stackoverflow.com/q/24093433 , который я голосую, чтобы вновь открыть. Они оба полезны, похожи, вопросы, но ответы весьма различны, поэтому было бы полезно разделить их.
Джереми Бэнкс

Ответы:


304

Если вы хотите проверить определенный тип, вы можете сделать следующее:

if let stringArray = obj as? [String] {
    // obj is a string array. Do something with stringArray
}
else {
    // obj is not a string array
}

Вы можете использовать «как!» и это вызовет ошибку времени выполнения, если objне имеет типа[String]

let stringArray = obj as! [String]

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

let items : [Any] = ["Hello", "World"]
for obj in items {
   if let str = obj as? String {
      // obj is a String. Do something with str
   }
   else {
      // obj is not a String
   }
}

Почему это только выдает ошибку времени выполнения, а не ошибку времени компиляции, когда ее ?нет. Похоже, asи ?при объединении будет выполнять проверку во время выполнения. Когда было бы целесообразно использовать asбез ?? Заранее спасибо.
Unheilig

@Unheilig Вы должны использовать только asбез ?if, если ваша программа не сможет восстановить объект, не относящийся к этому типу, потому что программа немедленно остановится, если это не так. Использование оператора ?in ifпозволяет программе продолжить.
drewag

Спасибо за ответ. Поправьте меня, если я ошибаюсь: я подумал, что использование ?в этом случае будет выполнять «общую» проверку типа, если да, для предложения if, если нет - для предложения else. Без этого ?никогда не будет введено и, как вы указали, вызвать ошибку во время выполнения. Еще раз спасибо.
Unheilig

@ Unheilig Извини, я не понимаю, что ты говоришь / спрашиваешь. Функция ?позволяет возвращать присваивание, nilвызывая возврат оператора if falseи, следовательно, переходя к оператору else. Тем не менее, я думаю, что объяснение помогает с пониманием, но if letна самом деле это особый случай в компиляторе
drewag

1
@Unheilig Правильно, вы можете использовать var, если хотите иметь возможность изменять значение, находясь в этой локальной области (эти изменения не затронут за пределами области)
drewag

202

В Swift 2.2 - 5 теперь вы можете делать:

if object is String
{
}

Затем для фильтрации вашего массива:

let filteredArray = originalArray.filter({ $0 is Array })

Если у вас есть несколько типов для проверки:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }

Это решение короче, но у него есть недостаток: вы не можете использовать его objectкак Stringфигурные скобки (по крайней мере, в Swift 2), а с помощью letрешения вы можете это сделать.
Ферран Майлинч

@FerranMaylinch Не понимаю, что вы имеете в виду, потому что использование objectв блоке хорошо.
значение имеет значение

@ смысл-имеет значение, например, вы не сможете сделать это, object.uppercaseStringпотому что тип переменной не приведен к этому типу, вы просто проверили, что объект (на который указывает переменная) - этоString
Ferran Maylinch

Как вы можете сделать это, если ваш тип класса, который вы проверяете, является произвольным? Если у вас есть только переменная, вам нужно получить тип класса?
Алексей Заватоне

152

Если вы хотите знать , если объект является подтип данного типа , то есть более простой подход:

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}

func area (shape: Shape) -> Double {
  if shape is Circle { ... }
  else if shape is Rectangle { ... }
}

«Используйте оператор проверки типа (is), чтобы проверить, принадлежит ли экземпляр определенному типу подкласса. Оператор проверки типа возвращает true, если экземпляр принадлежит к этому типу подкласса, и false, если это не так ». Выдержка из: Apple Inc. «Язык программирования Swift». интерактивные книги .

В вышесказанном важна фраза «определенного типа подкласса». Использование is Circleи is Rectangleпринимается компилятором, потому что это значение shapeобъявлено как Shape(суперкласс CircleиRectangle ).

Если вы используете примитивные типы, суперкласс будет Any. Вот пример:

 21> func test (obj:Any) -> String {
 22.     if obj is Int { return "Int" }
 23.     else if obj is String { return "String" }
 24.     else { return "Any" }
 25. } 
 ...  
 30> test (1)
$R16: String = "Int"
 31> test ("abc")
$R17: String = "String"
 32> test (nil)
$R18: String = "Any"

2
Что если я сохраню примитивный тип в массиве или если массив является одним из примитивных типов, все isравно будет работать здесь? Спасибо.
Unheilig

Это должно работать, если вы объявите objectкак Any. Обновлено с примером.
GoZoner

Спасибо за ответ. Это выглядит многообещающе. Мое единственное сомнение заключается в том, что согласно приведенному ниже ответу, в котором AnyObjectпредлагается, кажется, было высказано возражение из-за AnyObjectотсутствия наследования от NSObject. Если Anyвсе по-другому, то это было бы действительно отличным решением. Спасибо.
Unheilig

21

У меня есть 2 способа сделать это:

if let thisShape = aShape as? Square 

Или:

aShape.isKindOfClass(Square)

Вот подробный пример:

class Shape { }
class Square: Shape { } 
class Circle: Shape { }

var aShape = Shape()
aShape = Square()

if let thisShape = aShape as? Square {
    println("Its a square")
} else {
    println("Its not a square")
}

if aShape.isKindOfClass(Square) {
    println("Its a square")
} else {
    println("Its not a square")
}

Редактировать: 3 сейчас:

let myShape = Shape()
if myShape is Shape {
    print("yes it is")
}

1
isKindOfClassэто метод NSObjectпротокола; он должен работать только для классов, которые его принимают (все классы, происходящие из NSObject, плюс любой пользовательский класс Swift, который принимает его явно)
Николас Миари


9

Предположим, что drawTriangle является экземпляром UIView.To проверить, имеет ли drawTriangle тип UITableView:

В Swift 3 ,

if drawTriangle is UITableView{
    // in deed drawTriangle is UIView
    // do something here...
} else{
    // do something here...
}

Это также может быть использовано для классов, определенных вами. Вы можете использовать это, чтобы проверить подпредставления представления.


5

Почему бы не использовать встроенный функционал, созданный специально для этой задачи?

let myArray: [Any] = ["easy", "as", "that"]
let type = type(of: myArray)

Result: "Array<Any>"

Функция type () проста
:)

5

Будьте предупреждены об этом:

var string = "Hello" as NSString
var obj1:AnyObject = string
var obj2:NSObject = string

print(obj1 is NSString)
print(obj2 is NSString)
print(obj1 is String)
print(obj2 is String) 

Все четыре последние строки возвращают true, потому что если вы наберете

var r1:CGRect = CGRect()
print(r1 is String)

... он печатает "ложь", конечно, но Предупреждение говорит о том, что приведение из CGRect к String завершается неудачно. Таким образом, некоторые типы являются мостовыми, а ключевое слово is вызывает неявное приведение.

Вы должны лучше использовать один из них:

myObject.isKind(of: MyClass.self)) 
myObject.isMember(of: MyClass.self))

2

Если вы просто хотите проверить класс без предупреждения из-за неиспользуемого определенного значения (let someVariable ...), вы можете просто заменить let let на логическое значение:

if (yourObject as? ClassToCompareWith) != nil {
   // do what you have to do
}
else {
   // do something else
}

Xcode предложил это, когда я использовал let way и не использовал определенное значение.


2

Почему бы не использовать что-то подобное

fileprivate enum types {
    case typeString
    case typeInt
    case typeDouble
    case typeUnknown
}

fileprivate func typeOfAny(variable: Any) -> types {
    if variable is String {return types.typeString}
    if variable is Int {return types.typeInt}
    if variable is Double {return types.typeDouble}
    return types.typeUnknown
}

в Свифте 3.


2

Swift 4.2, в моем случае использую функцию isKind.

isKind (of :) Возвращает логическое значение, которое указывает, является ли получатель экземпляром данного класса или экземпляром какого-либо класса, который наследуется от этого класса.

  let items : [AnyObject] = ["A", "B" , ... ]
  for obj in items {
    if(obj.isKind(of: NSString.self)){
      print("String")
    }
  }

Подробнее https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind


1
Это не Свифт. Это какао и работает только там , где он будет работать на Objective C.
матовый

1

myObject as? Stringвозвращает, nilесли myObjectне является String. В противном случае он возвращает a String?, так что вы можете получить доступ к самой строке с помощью myObject!или myObject! as Stringбезопасно преобразовать ее.


1

Свифт 3:

class Shape {}
class Circle : Shape {}
class Rectangle : Shape {}

if aShape.isKind(of: Circle.self) {
}

1

Просто для полноты основанного на принятом ответе и некоторых других:

let items : [Any] = ["Hello", "World", 1]

for obj in items where obj is String {
   // obj is a String. Do something with str
}

Но вы также можете ( compactMapтакже «сопоставить» значения, которые filterне имеют):

items.compactMap { $0 as? String }.forEach{ /* do something with $0 */ ) }

И версия, использующая switch:

for obj in items {
    switch (obj) {
        case is Int:
           // it's an integer
        case let stringObj as String:
           // you can do something with stringObj which is a String
        default:
           print("\(type(of: obj))") // get the type
    }
}

Но придерживаясь вопроса, чтобы проверить, если это массив (то есть [String]):

let items : [Any] = ["Hello", "World", 1, ["Hello", "World", "of", "Arrays"]]

for obj in items {
  if let stringArray = obj as? [String] {
    print("\(stringArray)")
  }
}

Или в более общем плане (см. Ответ на этот другой вопрос ):

for obj in items {
  if obj is [Any] {
    print("is [Any]")
  }

  if obj is [AnyObject] {
    print("is [AnyObject]")
  }

  if obj is NSArray {
    print("is NSArray")
  }
}

1

as?не всегда даст вам ожидаемый результат , потому что asне проверяет , является ли тип данных является определенного рода , но только если тип данных может быть преобразован в или представить в виде определенного вида.

Рассмотрим этот код, например:

func handleError ( error: Error ) {
    if let nsError = error as? NSError {

Каждый тип данных, соответствующий Errorпротоколу, может быть преобразован в NSErrorобъект, поэтому это всегда будет успешным . Но это не значит, что errorна самом деле это NSErrorобъект или подкласс этого.

Правильная проверка типа будет:

func handleError ( error: Error ) {
    if type(of: error) == NSError.self {

Однако это проверяет только точный тип. Если вы хотите также включить подкласс NSError, вы должны использовать:

func handleError ( error: Error ) {
    if error is NSError.Type {

0

Если у вас есть ответ как этот:

{
  "registeration_method": "email",
  "is_stucked": true,
  "individual": {
    "id": 24099,
    "first_name": "ahmad",
    "last_name": "zozoz",
    "email": null,
    "mobile_number": null,
    "confirmed": false,
    "avatar": "http://abc-abc-xyz.amazonaws.com/images/placeholder-profile.png",
    "doctor_request_status": 0
  },
  "max_number_of_confirmation_trials": 4,
  "max_number_of_invalid_confirmation_trials": 12
}

и вы хотите проверить значение, is_stuckedкоторое будет читаться как AnyObject, все, что вам нужно сделать, это

if let isStucked = response["is_stucked"] as? Bool{
  if isStucked{
      print("is Stucked")
  }
  else{
      print("Not Stucked")
 }
}

0

Если вы не знаете, что в ответе от сервера вы получите массив словарей или отдельный словарь, вам нужно проверить, содержит ли результат массив или нет.
В моем случае всегда получаю массив словарей, кроме одного раза. Итак, чтобы справиться с этим, я использовал приведенный ниже код для Swift 3.

if let str = strDict["item"] as? Array<Any>

Вот как? Array проверяет, является ли полученное значение массивом (из словарных элементов). В противном случае вы можете обработать, если это единственный элемент словаря, который не хранится внутри массива.


0

Swift 5.2 и версия Xcode: 11.3.1 (11C504)

Вот мое решение проверки типа данных:

 if let typeCheck = myResult as? [String : Any] {
        print("It's Dictionary.")
    } else { 
        print("It's not Dictionary.") 
    }

Я надеюсь, что это поможет вам.


При ответе на старый вопрос ваш ответ был бы гораздо более полезным для других пользователей StackOverflow, если бы вы включили некоторый контекст для объяснения того, как ваш ответ помогает, особенно для вопроса, на который уже есть принятый ответ. Смотрите: Как мне написать хороший ответ .
Дэвид Бак
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.