Swift'te nesnenin sınıf adını dize olarak alın


320

Bir nesnenin sınıf adını şu şekilde almak String:

object_getClassName(myViewController)

böyle bir şey döndürür:

_TtC5AppName22CalendarViewController

Aradığım saf sürümü: "CalendarViewController". Bunun yerine sınıf adının temizlenmiş bir dizesini nasıl alabilirim ?

Bununla ilgili bazı soru girişimleri buldum ama gerçek bir cevap bulamadım. Hiç mümkün değil mi?


alternatif olarak… bunun için etkili bir ayrıştırıcı işlevi nasıl görünür?
Bernd

Bir nesnenin belirli bir sınıfta olup olmadığını kontrol etmek istiyorsanız bu cevaba bakınız .
anlam önemlidir

Yanıtlar:


529

Bir örnekten dize :

String(describing: YourType.self)

Bir türden dize :

String(describing: self)

Misal:

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"

İle test edilmiştir class, structve enum.


4
Gerçekten böyle mi? Bu başlatıcı için String belgelerine göre, Streamable veya CustomStringConvertible veya CustomDebugStringConvertible ile uyumlu olmadığında "Swift standart kitaplığı tarafından otomatik olarak belirtilmemiş bir sonuç sağlanır". Şimdi istenen değeri gösteriyor olsa da, gelecekte de yapmaya devam edecek mi?
Tod Cunningham

3
Bunu Xcode 7.3.1 ve Swift 2.2'de kullanmak aşağıdakileri üretir, ki bu ideal değildir. '' let name = Dize (kendi kendine) adı Dize "<MyAppName.MyClassName: 0x ########>" ''
CodeBender

13
Swift 3'te, doğru cevap String(describing: MyViewController.self)aşağıda birkaç cevapta belirtildiği gibi aramaktır.
AmitaiB

10
Ancak "MyViewController.TYPE" döndürür!
orkenstein

3
@ RudolfAdamkovič Evet, bu doğru olur. Ama (ben sınıfı yeniden adlandırmak Ne olursa açıkça sınıf adı yazılı sevmiyorum Fooiçin Foo2ancak bir sınıf oluşturmak Fooolduğunu kudreti frenin, başka yerde kodu). Alt sınıflama durumunda, ihtiyacınıza type(of:)veya Type.selfihtiyacınıza bağlı olarak kullanmanız gerekir. Muhtemelen type(of:)sınıf adını almak için daha uygundur.
Marián Černý

182

SWIFT 5'E GÜNCELLENDİ

Başlatıcı aracılığıyla örnek değişkenini kullanarak tür adlarının güzel açıklamalarını alabilir Stringve belirli bir sınıfın yeni nesnelerini oluşturabiliriz

Mesela print(String(describing: type(of: object))). Dizi, sözlük, an , a , vb. Gibi objectbir örnek değişkeni nerede olabilir?IntNSDate

NSObjectÇoğu Objective-C sınıfı hiyerarşisinin kök sınıfı olduğundan , NSObjecther alt sınıfının sınıf adını almak için bir uzantı oluşturmayı deneyebilirsiniz NSObject. Bunun gibi:

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

Veya parametresi tür olan Any(tüm türlerin örtük olarak uyumlu olduğu protokol) ve sınıf adını String olarak döndüren statik bir işlev yapabilirsiniz. Bunun gibi:

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

Şimdi böyle bir şey yapabilirsiniz:

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

    }
}

Ayrıca, sınıf adını String olarak alabildiğimizde, o sınıfın yeni nesnelerini başlatabiliriz :

// 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)

Belki bu birisine yardım eder !.


2
Bu kabul edilen cevap olmalı. Teşekkürler, sınıf adını somutlaştırmadan yazdırmanın bir yolunu arıyordum ve cevabı burada buldum. Thanks print (String (BaseAsyncTask))
Bruno Coelho

4
classNameAsStringbasitleştirilebilir className, çünkü bu "isim" türünü ima eder. theClassNamebaşlangıçta facebook olarak adlandırılan facebook'un hikayesine benzemektedir.
DawnSong

"dikte" ... Swift'in sözlük koleksiyonunun İtalyanca versiyonu:]
Andrew Kirna

Bu çözüm, her classTag için proje adını bekleyen, yani ProjectTest.MyViewController. Bu proje adından nasıl kurtulabilirim? Sadece sınıf adı istiyorum.
Sazzad Hissain Khan

132

Hızlı 5

İşte typeNamebir değişken olarak almak için uzantı (hem değer türü hem de referans türü ile çalışmak).

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

Nasıl kullanılır:

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

12
MyAppName.MyClassName'i neden bundan çıkardığım hakkında bir fikrin var mı? Ben sadece MyClassName ile ilgileniyorum
Andrew

@Andrew davanızda daha ayrıntılı bilgi verebilir misiniz? Bu uzantıyı birçok projede kullanıyorum ve hepsi amaçlandığı gibi yanıt veriyor.
nahung89

1
Sınıfın kendisinden çağırırsam MyClassName döndürür, ancak sınıf adı dizesini döndüren bir yöntemde sarar ve başka bir sınıftan çağırırsam MyAppName.MyClassName döndürür. Etrafında arama yaparken tüm sınıfların örtülü olarak ad alanı olduğunu gördüm, bu yüzden sınıfınızın dışında çağırırsanız MyClassName yerine MyAppName.MyClassName alırsınız. Daha sonra sınıf adını almak için dize manipülasyonuna güvenmeniz gerekir.
Andrew

1
çok yararlı uzatma
Hattori Hanzō

@Andrew, geçerli çalışma emin değilim, ama hatırlıyorum, String (description: type (of: self)) bir noktada ModuleName.ClassName döndürmek için kullanılır. Dayalı bölünmüştü. karakter ve son nesne getirildi.
BangOperator

31

Swift 3.0

String(describing: MyViewController.self)


2
Aslında type(of: )orada bir overkill, String(describing: MyViewController.self)yeterli olacak
Alexander Vasenin

2
Bu benim için <App_Name.ClassName: 0x79e14450> gibi bir dize oluşturur, bu ideal değildir
Doug Amos

Öyle değil mi String.init(describing: MyViewController.self)?
Iulian Onofrei

2
@IulianOnofrei, Bunu yapabilirsin, ama initgereksiz.
shim

Tamamen AppName önekinden kurtulun! Teşekkürler
yuchen

28

Böyle bir yaklaşım öneririm ( çok Swifty ):

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

Ne içgözlem ne de manuel demontaj kullanmaz ( sihir yok! ).


İşte bir 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

Swift 3.0'da değiştirdiler. Şimdi dize alabilirsiniztype(of: self)
Asad Ali

Swift 3'e güncellendi
werediver

Bu tür adı almak için en iyi yolu olduğunu düşünüyorum: NSObject, enum, typealiase, işlevler, var özellikleri ve herhangi bir tür, hatta sabitleri dayalı herhangi bir sınıf örneği. Türler için .self kullanmalısınız. Örneğin, typeName (Int.self), typeName (true.self). Ya da proje adı hakkında endişelenmeniz gerekmez (AppProj. [Typename] gibi) Harika!
David.Chu.ca

23

Türünüz varsa Foo, aşağıdaki kod size "Foo"Swift 3 ve Swift 4'te verecektir :

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

Buradaki cevapların çoğundaki sorun "Foo.Type", türün herhangi bir örneğine sahip olmadığınızda, gerçekten istediğiniz şey olduğunda, sonuçta ortaya çıkan dize olarak vermeleridir "Foo". Aşağıdakiler "Foo.Type", diğer cevapların bir kısmında belirtildiği gibi size verir .

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

type(of:)Eğer sadece bir parçası gereksizdir "Foo".


21

In Swift 4.1 ve şimdi 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)

Etrafınızda bir örneğiniz yoksa:

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

Etrafta bir örneğiniz varsa:

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

14

Bir nesneden Swift sınıfının adını almak için, örneğin var object için: SomeClass (),

String(describing: type(of: object))

Swift sınıfının adını sınıf türünden almak için, örneğin SomeClass, şunu kullanın:

String(describing: SomeClass.self)

Çıktı:

"SomeClass"


13

Bu şekilde deneyebilirsiniz:

self.classForCoder.description()

Tam "ad alanı" (uygun hedef üyelik öneki) de içerdiği için bu harika çalışıyor.
BonanzaDriver

BTW, ben de "String (self)" kullanarak kendini burada MyViewController bir örneği olduğu da bulundu ... aynı bilgileri sağlar ... Xcode 7.1.
BonanzaDriver

12

Swift standart kütüphane işlevini şu şekilde kullanabilirsiniz _stdlib_getDemangledTypeName:

let name = _stdlib_getDemangledTypeName(myViewController)

@NeilJaff "sadece isteğe bağlı bir dize" ile ne demek istiyorsun? Bir dize döndürür ve isteğe bağlı değildir.
Jernej Strasner

3
Bu yaklaşım, özel API FYI'deki sınıfların adlarını döndürmez. İlk ortak temel sınıf adının adını döndürür.
Tylerc230

İletiyi alıyorum: Çözülmemiş tanımlayıcı kullanımı '_stdlib_getDemangledTypeName'
Adobels

12

Swift 4'te (önceki sürümleri kontrol etmedim) dize olarak tür adını almak için , sadece dize enterpolasyonunu kullanın:

"\(type(of: myViewController))"

Sen kullanabilirsiniz .selfbir tip kendisi ve üzerinde type(of:_)bir örneğinde fonksiyonu:

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

8

Aynalar da kullanılabilir:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

Not: Bu yöntem, Yapılar ve Numaralandırmalar için de kullanılabilir. Yapının ne tür olduğunu gösteren bir displayStyle vardır:

Mirror(reflecting: vc).displayStyle

Dönüş bir enum'dur, böylece şunları yapabilirsiniz:

Mirror(reflecting: vc).displayStyle == .Class

Teşekkürler. Bu benim için işe yaradı. Tek şey (en azından iOS 9'da) topicType ".Type" ile bitiyor olmasıdır, bu yüzden dizeden bu kısmı kaldırmak zorunda kaldı.
Nikolay Suvandzhiev

8

Swift 5.1

Sınıf, yapı, numaralandırma, protokol ve NSObject adlarını alabilirsiniz Self.self.

print("\(Self.self)")

Bunun modül adının önüne farklı gelebileceğini unutmayın ( String(describing: type(of: self))
Ixx

7

Swift 3.0: Bunun gibi bir uzantı oluşturabilirsiniz .. Proje adı olmadan sınıf adını verir

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

    public class var className: String {
        return NSStringFromClass(self).components(separatedBy: ".").last ?? ""
    }
}

Bunu kullanmaya çalıştığımda hatayı alıyorum: 'ProjectName.MyViewController' (0x1020409e8) türünün değeri 'Swift.AnyObject.Type' (0x7fb96fc0dec0) 'a dökülemedi.
tylerSF

6

NSObjectProtocolSwift 4'te şöyle genişletebilirsiniz :

import Foundation

extension NSObjectProtocol {

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

Bu hesaplanan değişkeni classNameTÜM sınıflar için kullanılabilir hale getirecektir . Bir içinde bu kullanma print()in CalendarViewControlleryazdırır "CalendarViewController"konsolda.


4

Bir süredir bu cevabı arıyordum. GKStateMachine kullanıyorum ve durum değişikliklerini gözlemlemeyi seviyorum ve sadece sınıf adını görmenin kolay bir yolunu istedim. Sadece iOS 10 veya Swift 2.3 olup olmadığından emin değilim, ancak bu ortamda aşağıdakiler tam olarak istediğimi yapıyor:

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

// Output:    
// Class Name: GKState

4

Sınıfın adını şöyle yapabilirsiniz:

class Person {}
String(describing: Person.self)

3

Sınıf adını String olarak almak için sınıfınızı aşağıdaki gibi beyan edin

@objc(YourClassName) class YourClassName{}

Ve aşağıdaki sözdizimini kullanarak sınıf adını alın

NSStringFromClass(YourClassName)

3

Deneyin reflect().summarySınıf öz veya örnek dynamicType üzerinde. DynamicType almadan önce isteğe bağlı öğelerin paketini açın, aksi takdirde dynamicType isteğe bağlı sarmalayıcıdır.

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;

Özet, genel türlerin açıklandığı bir sınıf yoludur. Özetle basit bir sınıf adı almak için bu yöntemi deneyin.

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

3

Yukarıdaki çözümler benim için işe yaramadı. Üretilen konular çoğunlukla birkaç yorumda belirtilmiştir:

MyAppName.ClassName

veya

MyFrameWorkName.ClassName

Bu çözümler XCode 9, Swift 3.0 üzerinde çalıştı:

Bunu adında classNameCleanedgelecekte ile çatışma değil daha kolay erişilebilir olan ve yapar böylece className()değişiklikleri:

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

}

}

Kullanımı:

NSViewController.classNameCleaned
MyCustomClass.classNameCleaned

2

Swift 3.0 (macOS 10.10 ve üstü), className'den alabilirsiniz

self.className.components(separatedBy: ".").last!

1
Anlayabildiğim kadarıyla Swift sınıflarında yerleşik bir className özelliği yoktur. Bir şeyden alt sınıf mı yaptınız?
shim

@shim className Foundation'da bildirildi, ancak yalnızca macOS 10.10 ve sonraki sürümlerinde kullanılabilir gibi görünüyor. Cevabımı yeni düzenledim.
Thanh Vũ Trần

1
Hm, bu garip. Neden iOS'a eklemiyorlar? Ayrıca NSObject developer.apple.com/reference/objectivec/nsobject/…
shim

2

type(of:...)Playground'da Swift 3 ile denedim . Bu benim sonucum. Bu kod biçimi sürümüdür.

print(String(describing: type(of: UIButton.self)))
print(String(describing: type(of: UIButton())))
UIButton.Type
UIButton

Sadece sınıf adını belirlemek için bir örneği başlatmak israftır.
sethfri

@sethfri Dinamik türü belirlemek için bunu kullanıyorum.
Lumialxk

Neden sadece sınıfın tür bilgisini almak için bir örnek oluşturmanız gerektiğini anlamıyorum
sethfri


2

Bu tür bir örnek var. Paketin adını eklemeyin.

extension NSObject {
    class var className: String {
        return "\(self)"
    }
}

1

Karışık adı beğenmediyseniz, kendi adınızı belirtebilirsiniz:

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

Ancak, uzun vadede karışık adı ayrıştırmayı öğrenmek daha iyi olur. Biçim standart ve anlamlıdır ve değişmez.


Hayır, sınıfım için geri dönüyor (ExistentialMetatype) - belgelenmemiş çalışma zamanı davranışına güvenmek, özellikle nispeten erken bir aşamada olduğu için özellikle hızlıdır.
superarts.org

1

Bazen diğer çözümler, bakmaya çalıştığınız nesneye bağlı olarak yararlı olmayan bir ad verir. Bu durumda, sınıf adını aşağıdakileri kullanarak dize olarak alabilirsiniz.

String(cString: object_getClassName(Any!))

Useful oldukça yararlı olan bazı ilgili yöntemleri görmek için xcode fonksiyonunu tıklayın. veya buradan kontrol edin https://developer.apple.com/reference/objectivec/objective_c_functions


1

Swift 5 Dize ile class_name dizesini alabilirsiniz (tanımlayan: Self.self)

print(String(describing: Self.self))

0

Swift 2.2'de kullanıyorum

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

0

Hızlı 5.1: -

Nesnenin sınıf adını dize olarak almak için genel işlevi de kullanabilirsiniz

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

}

Aşağıdakileri kullanarak bu işlevi çağırın: -

let name = GenericFunctions.className(ViewController.self)

Mutlu Kodlama :)


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.