Dapatkan nama kelas objek sebagai string di Swift

320

Mendapatkan nama kelas suatu objek Stringmenggunakan:

object_getClassName(myViewController)

mengembalikan sesuatu seperti ini:

_TtC5AppName22CalendarViewController

Saya mencari murni versi: "CalendarViewController". Bagaimana cara mendapatkan string yang dibersihkan dari nama kelas saja?

Saya menemukan beberapa upaya pertanyaan tentang ini tetapi bukan jawaban yang sebenarnya. Apakah itu tidak mungkin sama sekali?

Bernd
sumber
alternatifnya ... seperti apa fungsi pengurai yang efektif untuk ini?
Bernd
Jika Anda ingin memeriksa apakah suatu objek dari kelas tertentu lihat jawaban ini .
maknanya penting

Jawaban:

529

String dari sebuah instance :

String(describing: YourType.self)

String dari suatu jenis :

String(describing: self)

Contoh:

struct Foo {

    // Instance Level
    var typeName: String {
        return String(describing: Foo.self)
    }

    // Instance Level - Alternative Way
    var otherTypeName: String {
        let thisType = type(of: self)
        return String(describing: thisType)
    }

    // Type Level
    static var typeName: String {
        return String(describing: self)
    }

}

Foo().typeName       // = "Foo"
Foo().otherTypeName  // = "Foo"
Foo.typeName         // = "Foo"

Diuji dengan class, structdan enum.

Rudolf Adamkovič
sumber
4
Benarkah ini masalahnya? Menurut dokumen untuk String untuk penginisialisasi ini "hasil yang tidak ditentukan disediakan secara otomatis oleh pustaka standar Swift" ketika itu tidak sesuai dengan Streamable, atau CustomStringConvertible, atau CustomDebugStringConvertible. Jadi, sementara itu mungkin menampilkan nilai yang diinginkan sekarang, apakah akan terus melakukannya di masa depan?
Tod Cunningham
3
Menggunakan ini dalam Xcode 7.3.1 & Swift 2.2 menghasilkan yang berikut, yang tidak ideal. '' let name = String (self) name String "<MyAppName.MyClassName: 0x ########>" "'
CodeBender
13
Dalam Swift 3, jawaban yang tepat adalah menelepon String(describing: MyViewController.self)seperti yang disebutkan dalam beberapa jawaban di bawah.
AmitaiB
10
Tetapi mengembalikan "MyViewController.TYPE"!
orkenstein
3
@ RudolfAdamkovič Ya, itu benar. Tapi saya tidak suka menulis nama kelas secara eksplisit (apa Jika saya mengubah nama kelas Foountuk Foo2tetapi membuat kelas Foolain, bahwa kekuatan rem kode). Dalam hal subklas Anda perlu menggunakan salah satu type(of:)atau Type.selftergantung apa yang sesuai dengan kebutuhan Anda. Mungkin type(of:)lebih tepat untuk mendapatkan nama kelas.
Marián Černý
182

DIPERBARUI UNTUK SWIFT 5

Kita bisa mendapatkan deskripsi yang cukup tentang nama tipe menggunakan variabel instance melalui Stringinitializer dan membuat objek baru dari kelas tertentu

Seperti, misalnya print(String(describing: type(of: object))). Di mana objectbisa menjadi variabel instan seperti array, kamus, an Int, a NSDate, dll.

Karena NSObjectadalah kelas dasar dari sebagian besar hierarki kelas Objective-C, Anda dapat mencoba membuat ekstensi untuk NSObjectmendapatkan nama kelas dari setiap subclass dari NSObject. Seperti ini:

extension NSObject {
    var theClassName: String {
        return NSStringFromClass(type(of: self))
    }
}

Atau Anda bisa membuat funcion statis yang parameternya bertipe Any(Protokol yang semua jenisnya sesuai) dan mengembalikan nama kelas sebagai String. Seperti ini:

class Utility{
    class func classNameAsString(_ obj: Any) -> String {
        //prints more readable results for dictionaries, arrays, Int, etc
        return String(describing: type(of: obj))
    }
} 

Sekarang Anda dapat melakukan sesuatu seperti ini:

class ClassOne : UIViewController{ /* some code here */ }
class ClassTwo : ClassOne{ /* some code here */ }

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Get the class name as String
        let dictionary: [String: CGFloat] = [:]
        let array: [Int] = []
        let int = 9
        let numFloat: CGFloat = 3.0
        let numDouble: Double = 1.0
        let classOne = ClassOne()
        let classTwo: ClassTwo? = ClassTwo()
        let now = NSDate()
        let lbl = UILabel()

        print("dictionary: [String: CGFloat] = [:] -> \(Utility.classNameAsString(dictionary))")
        print("array: [Int] = [] -> \(Utility.classNameAsString(array))")
        print("int = 9 -> \(Utility.classNameAsString(int))")
        print("numFloat: CGFloat = 3.0 -> \(Utility.classNameAsString(numFloat))")
        print("numDouble: Double = 1.0 -> \(Utility.classNameAsString(numDouble))")
        print("classOne = ClassOne() -> \((ClassOne).self)") //we use the Extension
        if classTwo != nil {
            print("classTwo: ClassTwo? = ClassTwo() -> \(Utility.classNameAsString(classTwo!))") //now we can use a Forced-Value Expression and unwrap the value
        }
        print("now = Date() -> \(Utility.classNameAsString(now))")
        print("lbl = UILabel() -> \(String(describing: type(of: lbl)))") // we use the String initializer directly

    }
}

Juga, begitu kita bisa mendapatkan nama kelas sebagai String, kita dapat membuat instance objek baru dari kelas itu :

// Instantiate a class from a String
print("\nInstantiate a class from a String")
let aClassName = classOne.theClassName
let aClassType = NSClassFromString(aClassName) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(String(cString: class_getName(type(of: instance))))
print(instance.self is ClassOne)

Mungkin ini membantu seseorang di luar sana!

mauricioconde
sumber
2
Ini harus menjadi jawaban yang diterima. Terima kasih, saya mencari cara untuk mencetak nama kelas tanpa harus instantiate, dan menemukan jawabannya di sini. Terima kasih cetak (String (BaseAsyncTask))
Bruno Coelho
4
classNameAsStringdapat disederhanakan untuk className, untuk "nama" tersirat tipenya. theClassNamemirip dengan kisah facebook, yang awalnya bernama facebook.
DawnSong
"diccionary" ... versi bahasa Italia dari koleksi kamus Swift:]
Andrew Kirna
Solusi ini menambahkan nama proyek untuk setiap classTag, yaitu ProjectTest.MyViewController. Bagaimana cara saya menyingkirkan nama proyek ini? Saya hanya ingin nama kelas.
Sazzad Hissain Khan
132

Cepat 5

Berikut ini ekstensi untuk mendapatkan typeNamesebagai variabel (berfungsi dengan tipe nilai atau tipe referensi).

protocol NameDescribable {
    var typeName: String { get }
    static var typeName: String { get }
}

extension NameDescribable {
    var typeName: String {
        return String(describing: type(of: self))
    }

    static var typeName: String {
        return String(describing: self)
    }
}

Cara Penggunaan:

// Extend with class/struct/enum...
extension NSObject: NameDescribable {}
extension Array: NameDescribable {}
extension UIBarStyle: NameDescribable { }

print(UITabBarController().typeName)
print(UINavigationController.typeName)
print([Int]().typeName)
print(UIBarStyle.typeName)

// Out put:
UITabBarController
UINavigationController
Array<Int>
UIBarStyle
nahung89
sumber
12
Adakah ide mengapa saya mendapatkan MyAppName.MyClassName dari itu? Saya hanya tertarik pada MyClassName
Andrew
@ Andrew dapat Anda berikan lebih spesifik dalam kasus Anda? Saya menggunakan ekstensi ini di banyak proyek dan semuanya merespons sebagaimana dimaksud.
nahung89
1
Yah jika saya memanggilnya dari kelas itu sendiri, ia mengembalikan MyClassName, tetapi jika saya membungkusnya dengan metode yang mengembalikan string nama kelas dan memanggilnya dari kelas lain ia mengembalikan MyAppName.MyClassName. Saat mencari di sekitar saya melihat bahwa semua kelas secara implisit namespace, jadi jika Anda menyebutnya di luar kelas Anda, Anda mendapatkan MyAppName.MyClassName bukan MyClassName. Maka Anda harus mengandalkan manipulasi string untuk mendapatkan nama kelas.
Andrew
1
ekstensi yang sangat berguna
Hattori Hanzo
@ Andrew, tidak yakin bekerja saat ini, tapi saya ingat, String (menggambarkan: tipe (dari: diri)) digunakan untuk mengembalikan ModuleName.ClassName pada satu titik. Saya telah membagi berdasarkan. karakter dan mengambil objek terakhir.
BangOperator
31

Swift 3.0

String(describing: MyViewController.self)

ramping
sumber
2
Sebenarnya type(of: )ada banyak pembunuhan di sana, String(describing: MyViewController.self)akan cukup
Alexander Vasenin
2
Ini membuat string seperti <App_Name.ClassName: 0x79e14450> bagi saya, yang tidak ideal
Doug Amos
Bukan String.init(describing: MyViewController.self)?
Iulian Onofrei
2
@IulianOnofrei, Anda bisa melakukan itu, tetapi inititu tidak perlu.
shim
Singkirkan sepenuhnya awalan AppName! Terima kasih
yuchen
28

Saya menyarankan pendekatan seperti itu ( sangat cepat ):

// Swift 3
func typeName(_ some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}

// Swift 2
func typeName(some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}

Itu tidak menggunakan introspeksi atau demangling manual ( tidak ada sihir! ).


Ini demo:

// Swift 3

import class Foundation.NSObject

func typeName(_ some: Any) -> String {
    return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}

class GenericClass<T> {
    var x: T? = nil
}

protocol Proto1 {
    func f(x: Int) -> Int
}


@objc(ObjCClass1)
class Class1: NSObject, Proto1 {
    func f(x: Int) -> Int {
        return x
    }
}

struct Struct1 {
    var x: Int
}

enum Enum1 {
    case X
}

print(typeName(GenericClass<Int>.self)) // GenericClass<Int>
print(typeName(GenericClass<Int>()))  // GenericClass<Int>

print(typeName(Proto1.self)) // Proto1

print(typeName(Class1.self))   // Class1
print(typeName(Class1())) // Class1
print(typeName(Class1().f)) // (Int) -> Int

print(typeName(Struct1.self)) // Struct1
print(typeName(Struct1(x: 1))) // Struct1
print(typeName(Enum1.self)) // Enum1
print(typeName(Enum1.X)) // Enum1
manusia
sumber
Mereka mengubahnya di Swift 3.0. Sekarang Anda bisa mendapatkan string daritype(of: self)
Asad Ali
Diperbarui ke Swift 3.
werediver
Saya pikir ini adalah cara terbaik untuk mendapatkan nama tipe: setiap instance dari kelas, berdasarkan pada NSObject, enum, typealiase, fungsi, properti var, dan semua tipe, bahkan konstanta. Untuk tipe, Anda harus menggunakan sendiri. Misalnya, typeName (Int.self), typeName (true.self). Anda juga tidak perlu khawatir tentang nama proyek sebagai bagian (seperti AppProj. [Typename]) Hebat!
David.Chu.ca
23

Jika Anda mengetik Foo, kode berikut akan memberi Anda "Foo"Swift 3 dan Swift 4:

let className = String(describing: Foo.self) // Gives you "Foo"

Masalah dengan sebagian besar jawaban di sini adalah bahwa mereka memberi Anda "Foo.Type"sebagai string yang dihasilkan ketika Anda tidak memiliki contoh jenis apa pun, ketika apa yang Anda inginkan sebenarnya adil "Foo". Berikut ini memberi Anda "Foo.Type", sebagaimana disebutkan dalam banyak jawaban lainnya.

let className = String(describing: type(of: Foo.self)) // Gives you "Foo.Type"

Bagian type(of:)itu tidak perlu jika Anda hanya ingin "Foo".

sethfri
sumber
21

Di Swift 4.1 dan sekarang Swift 4.2 :

import Foundation

class SomeClass {
    class InnerClass {
        let foo: Int

        init(foo: Int) {
            self.foo = foo
        }
    }

    let foo: Int

    init(foo: Int) {
        self.foo = foo
    }
}

class AnotherClass : NSObject {
    let foo: Int

    init(foo: Int) {
        self.foo = foo
        super.init()
    }
}

struct SomeStruct {
    let bar: Int

    init(bar: Int) {
        self.bar = bar
    }
}

let c = SomeClass(foo: 42)
let s = SomeStruct(bar: 1337)
let i = SomeClass.InnerClass(foo: 2018)
let a = AnotherClass(foo: 1<<8)

Jika Anda tidak memiliki instance sekitar:

String(describing: SomeClass.self) // Result: SomeClass
String(describing: SomeStruct.self) // Result: SomeStruct
String(describing: SomeClass.InnerClass.self) // Result: InnerClass
String(describing: AnotherClass.self) // Result: AnotherClass

Jika Anda memiliki instance sekitar:

String(describing: type(of: c)) // Result: SomeClass
String(describing: type(of: s)) // Result: SomeStruct
String(describing: type(of: i)) // Result: InnerClass
String(describing: type(of: a)) // Result: AnotherClass
McNight
sumber
14

Untuk mendapatkan nama kelas Swift dari objek, misalnya untuk objek var: SomeClass (), gunakan

String(describing: type(of: object))

Untuk mendapatkan nama kelas Swift dari tipe kelas, misalnya SomeClass, gunakan:

String(describing: SomeClass.self)

Keluaran:

"SomeClass"

Gurjit Singh
sumber
13

Anda dapat mencoba cara ini:

self.classForCoder.description()
陈方涛
sumber
Ini berfungsi dengan baik karena termasuk "namespace" lengkap (awalan keanggotaan target yang sesuai) juga.
BonanzaDriver
BTW, saya juga menemukan menggunakan "String (self)" di mana diri adalah turunan dari MyViewController bekerja juga ... itu memberikan informasi yang sama ... Xcode 7.1.
BonanzaDriver
12

Anda dapat menggunakan fungsi pustaka standar Swift yang disebut _stdlib_getDemangledTypeNameseperti ini:

let name = _stdlib_getDemangledTypeName(myViewController)
Jernej Strasner
sumber
@ NeilJaff apa yang Anda maksud dengan "hanya string opsional"? Ini mengembalikan string dan itu bukan opsional.
Jernej Strasner
3
Pendekatan ini tidak mengembalikan nama kelas di API pribadi FYI. Ini akan mengembalikan nama nama kelas dasar publik pertama.
Tylerc230
Saya mendapatkan pesan: Penggunaan pengidentifikasi yang tidak terselesaikan '_stdlib_getDemangledTypeName'
Adobels
12

Untuk mendapatkan nama jenis sebagai string di Swift 4 (saya belum memeriksa versi sebelumnya), cukup gunakan interpolasi string:

"\(type(of: myViewController))"

Anda bisa menggunakan .selftipe itu sendiri, dan type(of:_)fungsinya pada instance:

// Both constants will have "UIViewController" as their value
let stringFromType = "\(UIViewController.self)"
let stringFromInstance = "\(type(of: UIViewController()))"
Misha Karpenko
sumber
8

Anda juga dapat menggunakan mirror:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

NB: Metode ini juga dapat digunakan untuk Structs dan Enums. Ada displayStyle yang memberikan indikasi jenis struktur apa:

Mirror(reflecting: vc).displayStyle

Pengembalian adalah enum sehingga Anda dapat:

Mirror(reflecting: vc).displayStyle == .Class
Paul Ardeleanu
sumber
Terima kasih. Itu yang berhasil untuk saya. Satu-satunya hal (setidaknya pada iOS 9) adalah bahwa subjectType berakhir dengan ".Type", jadi saya harus menghapus bagian itu dari string.
Nikolay Suvandzhiev
8

Cepat 5.1

Anda bisa mendapatkan nama kelas, struct, enum, protokol dan NSObject Self.self.

print("\(Self.self)")
Victor Kushnerov
sumber
Perhatikan bahwa ini dapat menambahkan nama modul (berbeda ke String(describing: type(of: self)))
Ixx
7

Swift 3.0: Anda dapat membuat ekstensi seperti ini .. Ini mengembalikan nama kelas tanpa nama proyek

extension NSObject {
    var className: String {
        return NSStringFromClass(self as! AnyClass).components(separatedBy: ".").last ?? ""
    }

    public class var className: String {
        return NSStringFromClass(self).components(separatedBy: ".").last ?? ""
    }
}
Pengecualian
sumber
Ketika saya mencoba untuk menggunakan ini saya mendapatkan kesalahan: Tidak dapat memberikan nilai dari jenis 'ProjectName.MyViewController' (0x1020409e8) ke 'Swift.AnyObject.Type' (0x7fb96fc0dec0).
tylerSF
6

Anda dapat memperluas NSObjectProtocoldi Swift 4 seperti ini:

import Foundation

extension NSObjectProtocol {

    var className: String {
        return String(describing: Self.self)
    }
}

Ini akan membuat variabel terhitung classNametersedia untuk SEMUA kelas. Menggunakan ini di print()dalam CalendarViewControllerakan mencetak "CalendarViewController"di konsol.

Siddharth Bhatt
sumber
4

Saya sudah mencari jawaban ini berulang-ulang. Saya menggunakan GKStateMachine dan suka mengamati perubahan status dan ingin cara mudah untuk melihat hanya nama kelasnya. Saya tidak yakin apakah itu hanya iOS 10 atau Swift 2.3, tetapi di lingkungan itu, berikut ini melakukan persis apa yang saya inginkan:

let state:GKState?
print("Class Name: \(String(state.classForCoder)")

// Output:    
// Class Name: GKState
Troy
sumber
4

Anda bisa mendapatkan nama kelas melakukan sesuatu seperti:

class Person {}
String(describing: Person.self)
Ale Mohamad
sumber
3

Untuk mendapatkan nama kelas sebagai String mendeklarasikan kelas Anda sebagai berikut

@objc(YourClassName) class YourClassName{}

Dan dapatkan nama kelas menggunakan sintaks berikut

NSStringFromClass(YourClassName)
Moin Uddin
sumber
3

Coba reflect().summarydi Kelas mandiri atau contoh dinamis. Buka bungkusan opsional sebelum mendapatkan dynamicType, jika tidak, dynamicType adalah pembungkus opsional.

class SampleClass { class InnerClass{} }
let sampleClassName = reflect(SampleClass.self).summary;
let instance = SampleClass();
let instanceClassName = reflect(instance.dynamicType).summary;
let innerInstance = SampleClass.InnerClass();
let InnerInstanceClassName = reflect(innerInstance.dynamicType).summary.pathExtension;
let tupleArray = [(Int,[String:Int])]();
let tupleArrayTypeName = reflect(tupleArray.dynamicType).summary;

Ringkasan adalah jalur kelas dengan tipe generik yang dijelaskan. Untuk mendapatkan nama kelas yang sederhana dari ringkasan, cobalah metode ini.

func simpleClassName( complexClassName:String ) -> String {
    var result = complexClassName;
    var range = result.rangeOfString( "<" );
    if ( nil != range ) { result = result.substringToIndex( range!.startIndex ); }
    range = result.rangeOfString( "." );
    if ( nil != range ) { result = result.pathExtension; }
    return result;
}
ditarik ke depan
sumber
3

Solusi di atas tidak berhasil untuk saya. Sebagian besar masalah yang dihasilkan disebutkan dalam beberapa komentar:

MyAppName.ClassName

atau

MyFrameWorkName.ClassName

Solusi ini bekerja pada XCode 9, Swift 3.0:

Saya menamainya classNameCleanedsehingga lebih mudah diakses dan tidak bertentangan dengan className()perubahan di masa depan :

extension NSObject {

static var classNameCleaned : String {

    let className = self.className()
    if className.contains(".") {
        let namesArray = className.components(separatedBy: ".")
        return namesArray.last ?? className
    } else {
        return self.className()
    }

}

}

Pemakaian:

NSViewController.classNameCleaned
MyCustomClass.classNameCleaned
Darkwonder
sumber
2

Swift 3.0 (macOS 10.10 dan yang lebih baru), Anda bisa mendapatkannya dari className

self.className.components(separatedBy: ".").last!
Thanh Vũ Trần
sumber
1
Sejauh yang saya tahu tidak ada properti className bawaan di kelas Swift. Apakah Anda subkelas dari sesuatu?
shim
@shim className dideklarasikan di Foundation tetapi tampaknya itu hanya tersedia di macOS 10.10 dan yang lebih baru. Saya baru saja mengedit jawaban saya.
Thanh Vũ Trần
1
Hm, itu aneh. Mengapa mereka tidak memasukkannya ke iOS? Juga perhatikan ini adalah metode instan pada NSObject developer.apple.com/reference/objectivec/nsobject/…
shim
2

Saya mencoba type(of:...)di Playground dengan Swift 3. Ini adalah hasil saya. Ini adalah versi format kode.

print(String(describing: type(of: UIButton.self)))
print(String(describing: type(of: UIButton())))
UIButton.Type
UIButton
Lumialxk
sumber
Membuang instantiate instance hanya untuk menentukan nama kelas adalah sia-sia.
sethfri
@sethfri Saya menggunakan ini untuk menentukan tipe dinamis.
Lumialxk
Saya masih tidak mengerti mengapa Anda perlu membuat instance hanya untuk mendapatkan informasi tipe kelas
sethfri
2

Cepat 5

 NSStringFromClass(CustomClass.self)
Adobels
sumber
2

Contoh semacam ini untuk kelas var. Jangan sertakan nama bundel.

extension NSObject {
    class var className: String {
        return "\(self)"
    }
}
Ivan Tkachenko
sumber
1

Jika Anda tidak menyukai nama yang rusak, Anda dapat menentukan nama Anda sendiri:

@objc(CalendarViewController) class CalendarViewController : UIViewController {
    // ...
}

Namun, dalam jangka panjang akan lebih baik untuk belajar mengurai nama yang rusak. Formatnya standar dan bermakna dan tidak akan berubah.

matt
sumber
Tidak bukan, untuk kelas saya mengembalikan (ExistentialMetatype) - mengandalkan perilaku runtime tidak berdokumen selalu berbahaya, terutama untuk cepat karena masih dalam tahap yang relatif awal.
superarts.org
1

Terkadang solusi lain akan memberikan nama yang tidak berguna tergantung pada objek apa yang Anda coba lihat. Dalam hal ini Anda bisa mendapatkan nama kelas sebagai string menggunakan yang berikut ini.

String(cString: object_getClassName(Any!))

⌘ klik fungsi dalam xcode untuk melihat beberapa metode terkait yang cukup berguna. atau periksa di sini https://developer.apple.com/reference/objectivec/objective_c_functions

bob
sumber
1

Swift 5 Anda bisa mendapatkan class_name sebagai string dengan String (menggambarkan: Self.self)

print(String(describing: Self.self))
yasinkbas
sumber
0

Saya menggunakannya di Swift 2.2

guard let currentController = UIApplication.topViewController() else { return }
currentController.classForCoder.description().componentsSeparatedByString(".").last!
Alexander Khitev
sumber
0

Swift 5.1: -

Anda juga dapat menggunakan fungsi generik untuk mendapatkan nama kelas objek sebagai string

struct GenericFunctions {
 static func className<T>(_ name: T) -> String {
        return "\(name)"
    }

}

Panggil fungsi ini dengan menggunakan berikut: -

let name = GenericFunctions.className(ViewController.self)

Selamat Coding :)

dinesh sharma
sumber
0

Swift 5.2:

String(describing: type(of: self))
Abdelaziz Elrashed
sumber