Memeriksa apakah suatu objek adalah tipe yang diberikan dalam Swift


267

Saya memiliki array yang terdiri dari AnyObject. Saya ingin mengulanginya, dan menemukan semua elemen yang merupakan instance array.

Bagaimana saya bisa memeriksa apakah suatu objek dari tipe tertentu di Swift?


Pertanyaan Anda menanyakan tentang menemukan jenis objek yang diberikan, tetapi Anda telah menerima jawaban yang hanya mampu memeriksa apakah suatu objek jenis tertentu. Saya sarankan Anda mengedit pertanyaan Anda secara spesifik bahwa, jika tidak, banyak pembaca akan tidak puas dengan jawaban yang Anda terima. (Semua jawaban lain serupa, jadi untungnya Anda tidak perlu khawatir membuat mereka tidak valid dengan mempersempit pertanyaan Anda.)
Jeremy Banks

Saya telah mengedit pertanyaan ini untuk memisahkannya dari stackoverflow.com/q/24093433 , yang saya pilih untuk dibuka kembali. Keduanya sama-sama berguna, mirip, pertanyaan, tetapi jawabannya cukup berbeda sehingga akan berguna untuk memisahkan mereka.
Jeremy Banks

Jawaban:


304

Jika Anda ingin memeriksa jenis tertentu, Anda dapat melakukan hal berikut:

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

Anda dapat menggunakan "sebagai!" dan itu akan menimbulkan kesalahan runtime jika objbukan tipe[String]

let stringArray = obj as! [String]

Anda juga dapat memeriksa satu elemen sekaligus:

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
   }
}

Mengapa itu hanya melempar kesalahan runtime dan bukan kesalahan waktu kompilasi ketika ?tidak ada. Kedengarannya seperti asdan ?ketika digabungkan akan melakukan pemeriksaan runtime. Kapan akan tepat untuk digunakan astanpa ?? Terima kasih sebelumnya.
Unheilig

@Unheilig Anda hanya boleh menggunakan astanpa ?jika tidak ada cara program Anda bisa pulih dari objek yang tidak seperti itu karena program akan segera berhenti jika tidak. Menggunakan ?dalam ifpernyataan memungkinkan program untuk melanjutkan.
drewag

Terima kasih atas tanggapannya. Perbaiki saya jika saya salah: Saya pikir menggunakan ?dalam hal ini akan melakukan pemeriksaan tipe "generik", jika ya, ke klausa if, jika tidak, ke klausa lain. Tanpa yang ?lain tidak akan pernah dimasukkan dan saat Anda menunjukkan menyebabkan kesalahan runtime. Terima kasih lagi.
Unheilig

@Unheilig Maaf, saya tidak mengerti apa yang Anda katakan / tanyakan. The ?memungkinkan tugas untuk kembali nilmenyebabkan jika pernyataan untuk kembali falsedan karena itu jatuh melalui pernyataan lain. Namun, saya pikir penjelasan itu membantu dengan pemahaman, tetapi if letsebenarnya adalah kasus khusus dalam kompiler
drewag

1
@Unheilig Benar, Anda dapat menggunakan var jika Anda ingin dapat mengubah nilai sementara dalam lingkup lokal (perubahan-perubahan itu tidak akan mempengaruhi di luar lingkup)
drewag

202

Di Swift 2.2 - 5 sekarang Anda dapat melakukan:

if object is String
{
}

Kemudian untuk memfilter array Anda:

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

Jika Anda memiliki beberapa jenis untuk diperiksa:

    switch object
    {
    case is String:
        ...

    case is OtherClass:
        ...

    default:
        ...
    }

Solusi ini lebih pendek, tetapi memiliki kelemahan: Anda tidak dapat menggunakan objectsebagai Stringdi dalam kawat gigi (setidaknya di Swift 2), sedangkan dengan letsolusi Anda bisa melakukannya.
Ferran Maylinch

@FerranMaylinch Tidak mengerti maksud Anda karena menggunakan objectdi blok tidak masalah.
maknanya penting

@ arti-hal misalnya Anda tidak akan dapat melakukan object.uppercaseStringkarena jenis variabel tidak dicor ke jenis itu, Anda hanya memeriksa bahwa objek (ditunjukkan oleh variabel) adalahString
Ferran Maylinch

Bagaimana Anda bisa melakukan ini jika tipe kelas yang Anda periksa sewenang-wenang? Jika Anda hanya memiliki variabel, Anda perlu mendapatkan tipe kelas dari?
Alex Zavatone

152

Jika Anda hanya ingin tahu apakah suatu objek merupakan subtipe dari jenis yang diberikan maka ada pendekatan yang lebih sederhana:

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

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

“Gunakan operator pengecekan tipe untuk memeriksa apakah instance dari tipe subclass tertentu. Operator pemeriksa tipe mengembalikan true jika turunannya dari jenis subclass dan false jika tidak. " Kutipan dari: Apple Inc. “Bahasa Pemrograman Swift.” iBooks .

Dalam kalimat di atas frasa 'dari jenis subclass tertentu' adalah penting. Penggunaan is Circledan is Rectanglediterima oleh kompiler karena nilai shapetersebut dinyatakan sebagaiShape (superclass dari Circledan Rectangle).

Jika Anda menggunakan tipe primitif, superclass akan menjadi Any. Berikut ini sebuah contoh:

 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
Bagaimana jika saya menyimpan tipe primitif dalam array atau jika array adalah salah satu tipe primitif, apakah ismasih akan berfungsi di sini? Terima kasih.
Unheilig

Ini akan berfungsi jika Anda menyatakan objectsebagai Any. Diperbarui dengan sebuah contoh.
GoZoner

Terima kasih atas balasannya Itu terlihat menjanjikan. Satu-satunya keraguan saya adalah bahwa menurut jawaban di bawah ini, yang AnyObjectdisarankan, tampaknya telah dibalas karena AnyObjecttidak diwarisi dari NSObject. Jika Anyberbeda, maka ini sebenarnya akan menjadi solusi yang bagus juga. Terima kasih.
Unheilig

21

Saya punya 2 cara untuk melakukannya:

if let thisShape = aShape as? Square 

Atau:

aShape.isKindOfClass(Square)

Ini adalah contoh terperinci:

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")
}

Edit: 3 sekarang:

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

1
isKindOfClassadalah metode NSObjectprotokol; seharusnya hanya berfungsi untuk kelas yang mengadopsinya (semua kelas turun dari NSObject, ditambah kelas Swift khusus yang mengadopsinya secara eksplisit)
Nicolas Miari


9

Asumsikan drawTriangle adalah turunan dari UIView. Untuk memeriksa apakah drawTriangle bertipe UITableView:

Dalam Swift 3 ,

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

Ini juga dapat digunakan untuk kelas yang Anda tentukan sendiri. Anda bisa menggunakan ini untuk memeriksa subview tampilan.


5

Mengapa tidak menggunakan fungsionalitas bawaan yang dibangun khusus untuk tugas ini?

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

Result: "Array<Any>"

type () fungsi hanya sederhana
:)

5

Diperingatkan tentang ini:

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) 

Semua empat baris terakhir mengembalikan true, ini karena jika Anda mengetik

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

... itu mencetak "false" tentu saja, tetapi Peringatan mengatakan bahwa Cast dari CGRect ke String gagal. Jadi, beberapa jenis dijembatani, dan kata kunci 'is' memanggil pemeran implisit.

Anda sebaiknya menggunakan salah satu dari ini:

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

2

Jika Anda hanya ingin memeriksa kelas tanpa mendapat peringatan karena nilai yang didefinisikan tidak digunakan (biarkan someVariable ...), Anda dapat dengan mudah mengganti barang yang dibiarkan dengan boolean:

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

Xcode mengusulkan ini ketika saya menggunakan cara membiarkan dan tidak menggunakan nilai yang ditentukan.


2

Mengapa tidak menggunakan sesuatu seperti ini

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
}

dalam Swift 3.


2

Swift 4.2, Dalam kasus saya, menggunakan fungsi isKind.

isKind (of :) Mengembalikan nilai Boolean yang menunjukkan apakah penerima adalah turunan dari kelas yang diberikan atau turunan dari kelas apa pun yang mewarisi dari kelas itu.

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

Readmore https://developer.apple.com/documentation/objectivec/nsobjectprotocol/1418511-iskind


1
Itu bukan Swift. Ini adalah Kakao dan hanya berfungsi di mana ia akan bekerja untuk Objective C.
matt

1

myObject as? Stringkembali niljika myObjectbukan String. Jika tidak, ia mengembalikan a String?, sehingga Anda dapat mengakses string itu sendiri myObject!, atau melemparkannya dengan myObject! as Stringaman.


1

Swift 3:

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

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

1

Hanya demi kelengkapan berdasarkan jawaban yang diterima dan beberapa lainnya:

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

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

Tetapi Anda juga bisa ( compactMapjuga "memetakan" nilai-nilai yang filtertidak):

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

Dan versi menggunakan 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
    }
}

Tetapi berpegang pada pertanyaan, untuk memeriksa apakah itu array (yaitu [String]):

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

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

Atau lebih umum (lihat jawaban pertanyaan lain ini ):

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?tidak akan selalu memberikan hasil yang diharapkan karena astidak uji apakah tipe data adalah dari jenis tertentu tetapi hanya jika tipe data dapat dikonversi ke atau direpresentasikan sebagai jenis tertentu.

Pertimbangkan kode ini misalnya:

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

Setiap tipe data yang sesuai dengan Errorprotokol dapat dikonversi ke NSErrorobjek, jadi ini akan selalu berhasil . Namun itu tidak berarti bahwa errorpada kenyataannya NSErrorobjek atau subkelas dari itu.

Jenis cek yang benar adalah:

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

Namun, ini hanya memeriksa untuk jenis yang tepat. Jika Anda ingin juga menyertakan subkelas dari NSError, Anda harus menggunakan:

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

0

Jika Anda Memiliki Respons Seperti Ini:

{
  "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
}

dan Anda ingin memeriksa nilai is_stuckedyang akan dibaca sebagai AnyObject, yang harus Anda lakukan adalah ini

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

0

Jika Anda tidak tahu bahwa Anda akan mendapatkan array kamus atau kamus tunggal dalam respons dari server, Anda perlu memeriksa apakah hasilnya berisi array atau tidak.
Dalam kasus saya selalu menerima array kamus kecuali sekali. Jadi, untuk mengatasinya saya menggunakan kode di bawah ini untuk swift 3.

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

Di sini sebagai? Array memeriksa apakah nilai yang diperoleh adalah array (dari item kamus). Dalam kasus lain, Anda dapat menangani jika item kamus tunggal yang tidak disimpan di dalam array.


0

Versi Swift 5.2 & Xcode: 11.3.1 (11C504)

Inilah solusi saya untuk memeriksa tipe data:

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

Saya harap ini akan membantu Anda.


Saat menjawab pertanyaan lama, jawaban Anda akan jauh lebih berguna bagi pengguna StackOverflow lain jika Anda menyertakan beberapa konteks untuk menjelaskan bagaimana jawaban Anda membantu, terutama untuk pertanyaan yang sudah memiliki jawaban yang diterima. Lihat: Bagaimana saya menulis jawaban yang bagus .
David Buck
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.