Перевірка, чи є об'єкт даного типу в Swift


267

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

Як я можу перевірити, чи є об'єкт даного типу в Swift?


Ваше запитання задає питання про пошук даного об'єкта, але ви прийняли відповідь, яка здатна перевірити, чи є об'єкт даного типу. Я пропоную вам відредагувати саме це питання, інакше багато читачів будуть незадоволені відповіді, яку ви прийняли. (Усі інші відповіді схожі, тому на щастя вам не потрібно турбуватися про те, щоб зробити їх недійсними, звузивши своє запитання.)
Jeremy Banks

Я відредагував це питання, щоб відключити його від 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операторі дозволяє програмі продовжувати роботу.
drewag

Дякуємо за відповідь Виправте мене, якщо я помиляюся: я подумав, що використання ?цього випадку виконає перевірку типу "загального", якщо так, до пункту "if", якщо ні - до "іншого". Без ?іншого ніколи не буде введено, і, як ви вказали, викликати помилку виконання. Знову дякую.
Unheilig

@Unheilig Вибачте, я не розумію, що ви говорите / запитуєте. ?Дозволяє присвоювання повернутися nilвикликаючи якщо заяву повернення falseі , отже , провалюючись в іншу заяву. Однак я думаю, що пояснення допомагає з розумінням, але 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
Ферран Мейлінч

Як ви можете це зробити, якщо тип вашого класу, на який ви перевіряєте, довільний? Якщо у вас є лише змінна, вам потрібно отримати тип класу?
Олексій Заватоне

152

Якщо ви хочете лише знати, чи об’єкт є підтипом даного типу, тоді існує більш простий підхід:

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

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

“Використовуйте оператор перевірки типу (є), щоб перевірити, чи є примірник певного типу підкласу. Оператор перевірки типу повертає true, якщо екземпляр цього типу підкласу, а false, якщо його немає. " Уривок: Apple Inc. "Мова швидкого програмування". iBooks .

У зазначеному вище словосполучення "певного типу підкласу" є важливим. Використання 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

У мене є два способи це зробити:

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, який явно приймає його)
Nicolas Miari


9

Припустимо drawTriangle - це примірник UIView. Щоб перевірити, чи 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) 

Усі чотири останні рядки повертаються як істинні, це тому, що якщо ви вводите текст

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

... він, звичайно, друкує "помилково", але попередження говорить про те, що передача від CGRect до String не вдається. Таким чином, якийсь тип з'єднаний, а ключове слово "є" викликає неявний склад.

Вам краще скористатися одним із таких:

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

2

Якщо ви просто хочете перевірити клас, не отримуючи попередження через невикористане визначене значення (нехай деякіVariable ...), ви можете просто замінити пусті речі булевими:

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

Xcode запропонував це, коли я використовував пусковий шлях і не використовував визначене значення.


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
}

у Swift 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
Це не Свіфт. Це Какао і працює лише там, де він би працював на «Ціль 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

Якщо ви не знаєте, що у відповіді від сервера ви отримаєте масив словників чи єдиний словник, вам потрібно перевірити, чи містить результат масив чи ні.
У моєму випадку завжди отримується масив словників, крім одного разу. Отже, для обробки я використовував наведений нижче код для швидкого 3.

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

Тут як? Масив перевіряє, чи є отримане значення масивом (елементів словника). В іншому випадку ви можете обробити, якщо це єдиний елемент словника, який не зберігається всередині масиву.


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.