Bagaimana Anda mengetahui jenis objek (dalam Swift)?

255

Saat mencoba memahami suatu program, atau dalam beberapa kasus, berguna untuk dapat benar-benar mengetahui jenis sesuatu itu. Saya tahu debugger dapat menunjukkan kepada Anda beberapa jenis informasi, dan Anda biasanya dapat mengandalkan inferensi tipe untuk lolos dengan tidak menentukan jenis dalam situasi tersebut, tapi tetap saja, saya benar-benar ingin memiliki sesuatu seperti Python.type()

dynamicType (lihat pertanyaan ini )

Pembaruan: ini telah diubah di versi terbaru Swift, obj.dynamicTypesekarang memberi Anda referensi ke tipe dan bukan turunan tipe dinamis.

Yang ini sepertinya yang paling menjanjikan, tapi sejauh ini saya belum bisa menemukan tipe yang sebenarnya

class MyClass {
    var count = 0
}

let mc = MyClass()

# update: this now evaluates as true
mc.dynamicType === MyClass.self

Saya juga mencoba menggunakan referensi kelas untuk instantiate objek baru, yang tidak bekerja, tapi anehnya memberi saya sebuah kesalahan mengatakan saya harus menambahkan requiredinitializer:

bekerja:

class MyClass {
    var count = 0
    required init() {
    }
}

let myClass2 = MyClass.self
let mc2 = MyClass2()

Masih hanya langkah kecil menuju benar-benar menemukan jenis objek yang diberikan sekalipun

edit : Saya telah menghapus sejumlah besar detail yang sekarang tidak relevan - lihat riwayat edit jika Anda tertarik :)

Jiaaro
sumber
1
kemungkinan duplikat dari Cara mendapatkan kelas variabel di Swift?
David Berry
1
Menariknya, print(mc)atau dump(mc)akan mencetak ringkasan (yang bisa Anda dapatkan dari toString(mc)atau reflect(mc).summary), yang akan berisi nama kelas di suatu tempat. Tetapi tidak jelas bagaimana cara mendapatkan nama kelas sendiri.
Newacct
@ David mirip, tetapi tidak semua variabel adalah instance kelas. Juga pertanyaan itu benar-benar tentang memeriksa apakah jenis cocok dengan apa yang dicari oleh programmer, sedangkan saya berharap untuk hanya mengetahui jenis grosir
Jiaaro

Jawaban:

284

Versi Swift 3:

type(of: yourObject)
Jérémy Lapointe
sumber
8
Fakta yang menyenangkan. Ini tidak berfungsi dengan opsional yang secara terbuka tidak terbuka! yaitu var myVar: SomeType!. Kompiler memberikan kesalahan "Tidak dapat mengonversi nilai tipe 'SomeType! .Type' (alias 'ImplisitlyUnwrappedOptional <SomeType> .Type') ke tipe argumen yang diharapkan 'AnyClass' (alias 'AnyObject.Type') Kompiler menyarankan menambahkan as! AnyClasssetelah jenis tetapi kemudian Program macet dengan beberapa "EXC_BAD_INSTRUCTION" dan jiberrish lain yang tidak dapat saya pecahkan.
LightningStryk
1
Memang, ini harus menjadi jawaban yang diterima sekarang karena Swift 3 ada. Jeremy terima kasih!
biomiker
1
Jika Anda mencari nama tipe spesifik, ketika tipe itu tipe protokol, ini mungkin tidak cocok untuk Anda.
Chris Prince
2
Jika Anda memiliki Stringyang dilewatkan sebagai tipe Anymaka type(of:)akan ditampilkan Any, tidak String.
ScottyBlades
1
@ScottyBlades jadi apa solusinya. Bisakah kamu menyediakan?
Mubin Mall
109

Swift 2.0:

Cara yang tepat untuk melakukan introspeksi jenis ini adalah dengan struct Mirror ,

    let stringObject:String = "testing"
    let stringArrayObject:[String] = ["one", "two"]
    let viewObject = UIView()
    let anyObject:Any = "testing"

    let stringMirror = Mirror(reflecting: stringObject)
    let stringArrayMirror = Mirror(reflecting: stringArrayObject)
    let viewMirror = Mirror(reflecting: viewObject)
    let anyMirror = Mirror(reflecting: anyObject)

Kemudian untuk mengakses tipe itu sendiri dari Mirrorstruct Anda akan menggunakan properti subjectTypeseperti:

    // Prints "String"
    print(stringMirror.subjectType)

    // Prints "Array<String>"
    print(stringArrayMirror.subjectType)

    // Prints "UIView"
    print(viewMirror.subjectType)

    // Prints "String"
    print(anyMirror.subjectType)

Anda kemudian dapat menggunakan sesuatu seperti ini:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }
Gudbergur
sumber
7
Ini bagus. Hati-hati bahwa jika objek yang dicerminkan adalah tipe opsional, kemudian membandingkannya dengan tipe non-opsional akan gagal. Stringdan Optional(String)tidak sama.
Thomas Verbeek
1
Persis apa yang saya cari, ingin tahu apa jenis objek
Joseph
Apakah ada jenis perbandingan dalam konteks ini yang tidak akan gagal saat membandingkan opsional dengan jenis non-opsional?
Chris Prince
Itu yang saya cari. @Gudbergur terima kasih.
Mubin Mall
60

The dynamicType.printClassNamekode dari contoh dalam buku Swift. Tidak mungkin saya tahu untuk langsung mengambil nama kelas khusus, tetapi Anda dapat memeriksa jenis instance menggunakan iskata kunci seperti yang ditunjukkan di bawah ini. Contoh ini juga menunjukkan bagaimana menerapkan fungsi className kustom, jika Anda benar-benar ingin nama kelas sebagai string.

class Shape {
    class func className() -> String {
        return "Shape"
    }
}

class Square: Shape {
    override class func className() -> String {
        return "Square"
    }
}

class Circle: Shape {
    override class func className() -> String {
        return "Circle"
    }
}

func getShape() -> Shape {
    return Square() // hardcoded for example
}

let newShape: Shape = getShape()
newShape is Square // true
newShape is Circle // false
newShape.dynamicType.className() // "Square"
newShape.dynamicType.className() == Square.className() // true

catatan:
bahwa subclass NSObjectsudah mengimplementasikan fungsi className mereka sendiri. Jika Anda bekerja dengan Cocoa, Anda bisa menggunakan properti ini.

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()
Berlari
sumber
2
Hei, tidak yakin kapan itu berubah, tetapi seperti yang ditunjukkan Alex Pretzlav, perilakunya telah berubah.
Jiaaro
1
Iya. Pada Swift 3.0, subjectTypetidak lagi tersedia, dan dynamicTypemenyebabkan pesan penghentian dari kompiler.
Raphael
41

Pada Xcode 6.0.1 (setidaknya, tidak yakin kapan mereka menambahkannya), contoh asli Anda sekarang berfungsi:

class MyClass {
    var count = 0
}

let mc = MyClass()
mc.dynamicType === MyClass.self // returns `true`

Memperbarui:

Untuk menjawab pertanyaan asli, Anda benar-benar dapat menggunakan runtime Objective-C dengan objek Swift polos berhasil.

Coba yang berikut ini:

import Foundation
class MyClass { }
class SubClass: MyClass { }

let mc = MyClass()
let m2 = SubClass()

// Both of these return .Some("__lldb_expr_35.SubClass"), which is the fully mangled class name from the playground
String.fromCString(class_getName(m2.dynamicType))
String.fromCString(object_getClassName(m2))
// Returns .Some("__lldb_expr_42.MyClass")
String.fromCString(object_getClassName(mc))
Alex Pretzlav
sumber
Sepertinya mereka mengubahnya untuk memberi Anda jenis bukannya contoh.
Jiaaro
@Jaroaro, saya memperbarui jawaban saya dengan apa yang saya pikir Anda cari dalam pertanyaan awal Anda
Alex Pretzlav
36

Jika Anda hanya perlu memeriksa apakah variabel bertipe X, atau itu sesuai dengan beberapa protokol, maka Anda dapat menggunakan is, atau as?seperti berikut ini:

var unknownTypeVariable =if unknownTypeVariable is <ClassName> {
    //the variable is of type <ClassName>
} else {
    //variable is not of type <ClassName>
}

Ini sama dengan isKindOfClassdi Obj-C.

Dan ini setara dengan conformsToProtocol, atauisMemberOfClass

var unknownTypeVariable =if let myClass = unknownTypeVariable as? <ClassName or ProtocolName> {
    //unknownTypeVarible is of type <ClassName or ProtocolName>
} else {
    //unknownTypeVariable is not of type <ClassName or ProtocolName>
}
Valerii Lider
sumber
Bagian kedua dari jawaban Anda salah. Pernyataan 'jika dibiarkan' dengan as?pemeran bersyarat melakukan hal yang sama isKindOfClassjuga, hanya juga memberikan hasil pemeran jika itu Berhasil.
awolf
Setara isMemberOfClassdengan kondisi object.dynamicType == ClassName.self.
awolf
18

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}
Peter
sumber
Saya pikir isada dari sebelum Swift 3 ...?
Nicolas Miari
10

Untuk Swift 3.0

String(describing: <Class-Name>.self)

Untuk Swift 2.0 - 2.3

String(<Class-Name>)
techloverr
sumber
1
Yang penting tentang jawaban ini yang benar bagi saya, adalah string yang dihasilkan sama persis dengan nama kelas - jadi saya bisa menggunakan ini untuk mendapatkan nama entitas Data Inti dari subkelas NSManagedObject. Saya menggunakan versi Swift3.
Kendall Helmstetter Gelner
8

Berikut adalah 2 cara yang saya sarankan 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")
}
Esqarrouth
sumber
2
print( aShape is Square ), isoperator lebih disukai.
DawnSong
Solusi bagus bagi saya untuk mendapatkan jenis benda.
nihasmata
1

Tergantung pada use case. Tapi mari kita asumsikan Anda ingin melakukan sesuatu yang berguna dengan tipe "variabel" Anda. switchPernyataan Swift sangat kuat dan dapat membantu Anda mendapatkan hasil yang Anda cari ...

    let dd2 = ["x" : 9, "y" : "home9"]
    let dds = dd2.filter {
        let eIndex = "x"
        let eValue:Any = 9
        var r = false

        switch eValue {
        case let testString as String:
            r = $1 == testString
        case let testUInt as UInt:
            r = $1 == testUInt
        case let testInt as Int:
            r = $1 == testInt
        default:
            r = false
        }

        return r && $0 == eIndex
    }

Dalam hal ini, miliki kamus sederhana yang berisi pasangan kunci / nilai yang dapat berupa UInt, Int atau String. Dalam .filter()metode pada kamus, saya perlu memastikan saya menguji nilai-nilai dengan benar dan hanya menguji untuk String ketika itu adalah string, dll. Pernyataan beralih membuat ini sederhana dan aman! Dengan menetapkan 9 ke variabel tipe Any, itu membuat saklar untuk mengeksekusi Int. Coba ubah ke:

   let eValue:Any = "home9"

..dan coba lagi. Kali ini mengeksekusi as Stringkasus ini.

CPD
sumber
1

Jika Anda mendapat peringatan "selalu benar / gagal", Anda mungkin perlu memberikan peringatan apa pun sebelum menggunakan is

(foo as Any) is SomeClass
Josef
sumber
0
//: Playground - noun: a place where people can play

import UIKit

class A {
    class func a() {
        print("yeah")
    }

    func getInnerValue() {
        self.dynamicType.a()
    }
}

class B: A {
    override class func a() {
        print("yeah yeah")
    }
}

B.a() // yeah yeah
A.a() // yeah
B().getInnerValue() // yeah yeah
A().getInnerValue() // yeah
Avi Cohen
sumber