オブジェクトのクラス名をSwiftの文字列として取得します


320

オブジェクトのクラス名を取得する String使用し:

object_getClassName(myViewController)

このようなものを返します:

_TtC5AppName22CalendarViewController

純粋なバージョンを探しています:"CalendarViewController"ます。代わりに、クラス名のクリーンアップされた文字列を取得するにはどうすればよいですか?

これに関する質問の試みがいくつか見つかりましたが、実際の答えは見つかりませんでした。それはまったく不可能ですか?


あるいは...これに効果的なパーサー関数はどのようなものでしょうか?
Bernd 2014年

オブジェクトが特定のクラスのものかどうかを確認する場合は、この回答を参照してください。
意味の問題

回答:


529

インスタンスからの文字列:

String(describing: YourType.self)

タイプからの文字列:

String(describing: self)

例:

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"

でテストされclassstructそしてenum


4
これは本当ですか?この初期化子のStringのドキュメントによると、Streamable、CustomStringConvertible、またはCustomDebugStringConvertibleに準拠していない場合、「Swift標準ライブラリによって未指定の結果が自動的に提供されます」。それで、現在は目的の値を表示している可能性がありますが、今後も表示し続けますか?
Tod Cunningham

3
Xcode 7.3.1とSwift 2.2でこれを使用すると、次のようになりますが、これは理想的ではありません。'' let name = String(self)name String "<MyAppName.MyClassName:0x ########>" ''
CodeBender

13
Swift 3では、適切な答えは、String(describing: MyViewController.self)以下のいくつかの回答に記載されているように呼び出すことです。
AmitaiB 2016年

10
しかし、「MyViewController.TYPE」を返します!
orkenstein 16

3
@RudolfAdamkovičはい、そうです。ただし、クラス名を明示的に記述するのは好きではありません(クラスの名前をに変更FooしたFoo2が、Foo他の場所にクラスを作成すると、コードが壊れる可能性があります)。サブクラスの場合、次のいずれかを使用する必要があるtype(of:)か、Type.selfあなたの必要性に合うものを依存します。type(of:)クラス名を取得するには、おそらくより適切です。
マリアンチェルニー

182

SWIFT 5に更新

イニシャライザを通じてインスタンス変数使用して型名のきれいな説明を取得し、特定のクラスの新しいオブジェクトを作成できますString

のように、例えばprint(String(describing: type(of: object)))。ここでobjectあってもよいインスタンス変数アレイ、辞書、等IntNSDate

のでNSObject、ほとんどのObjective-Cのクラス階層のルートクラスである、あなたがのために拡張するために試みることができるNSObjectのすべてのサブクラスのクラス名を取得するにNSObject。このような:

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

または、パラメータがタイプAny(すべてのタイプが暗黙的に準拠するプロトコル)である静的関数を作成し、クラス名をStringとして返すこともできます。このような:

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

今、あなたはこのようなことをすることができます:

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

    }
}

また、クラス名をStringとして取得できたら、そのクラスの新しいオブジェクトをインスタンス化できます

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

たぶんこれは誰かを助けるでしょう!


2
これは受け入れられる答えになるはずです。おかげで、インスタンス化せずにクラス名を出力する方法を探していたところ、ここで答えが見つかりました。おかげでprint(String(BaseAsyncTask))
Bruno Coelho

4
classNameAsStringclassNameその「名前」はそのタイプを暗示しているため、に簡略化できます。theClassNameは元々thefacebookと名付けられたfacebookのストーリーに似ています。
DawnSong 2016年

"diccionary" ... Swiftの辞書コレクションのイタリア語版:]
Andrew Kirna

このソリューションは、各classTagのプロジェクト名を先頭に追加しProjectTest.MyViewControllerます。このプロジェクト名を削除するにはどうすればよいですか?クラス名だけ欲しい。
Sazzad Hissain Khan、

132

スウィフト5

これは、typeName変数として取得するための拡張です(値タイプまたは参照タイプの両方で機能します)。

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

使い方:

// 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を取得する理由は何ですか?私はMyClassNameにのみ興味があります
Andrew

@アンドリュー、あなたのケースでもっと具体的に教えてもらえますか?私は多くのプロジェクトでこの拡張機能を使用していますが、それらはすべて意図したとおりに応答します。
nahung89 2016年

1
クラス自体から呼び出すとMyClassNameが返されますが、クラス名の文字列を返すメソッドにラップして別のクラスから呼び出すと、MyAppName.MyClassNameが返されます。周りを検索していると、すべてのクラスに暗黙的に名前空間が付けられていることがわかりました。そのため、クラスの外部で呼び出すと、MyClassNameではなくMyAppName.MyClassNameが取得されます。次に、クラス名を取得するために文字列操作に依存する必要があります。
Andrew

1
非常に便利な拡張機能
服部半蔵

@Andrew、現在の動作は不明ですが、ある時点でModuleName.ClassNameを返すために使用されたString(describing:type(of:self))を覚えています。に基づいて分割しました。文字と最後のオブジェクトをフェッチしました。
BangOperator

31

Swift 3.0

String(describing: MyViewController.self)


2
実際にtype(of: )はそこにString(describing: MyViewController.self)は行き過ぎです、
それで

2
これにより、<App_Name.ClassName:0x79e14450>などの文字列が作成されますが、これは理想的ではありません
Doug Amos

ですString.init(describing: MyViewController.self)よね?
Iulian Onofrei

2
@IulianOnofrei、あなたはそれを行うことができますが、それinitは不要です。
シム

AppNameプレフィックスを完全に取り除く!ありがとう
yuchen 2017

28

私はそのようなアプローチを提案します(非常に迅速):

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

イントロスペクションも手動のデマングル化も使用しません(魔法はありません)。


ここにデモがあります:

// 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でそれを変更しました。文字列を取得できるようになりましたtype(of: self)
Asad Ali

スウィフト3にアップデート
werediver

これが型名を取得する最良の方法だと思います。NSObject、enum、typealiase、関数、varプロパティ、および定数などの型に基づくクラスのインスタンスです。タイプの場合、.selfを使用する必要があります。たとえば、typeName(Int.self)、typeName(true.self)などです。一部としてプロジェクト名を心配する必要もありません(AppProj。[typename]など)。
David.Chu.ca 2018年

23

タイプがあるFoo場合、次のコードは"Foo"Swift 3とSwift 4で使用できます。

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

ここでのほとんどの回答の問題"Foo.Type"は、タイプのインスタンスがなく、本当に必要なものがただの場合に、結果の文字列として提供されることです"Foo""Foo.Type"他の答えの束で述べたように、以下はあなたに与えます。

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

type(of:)欲しいだけならその部分は不要です"Foo"


21

スウィフト4.1と今スウィフト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)

周りにインスタンスがない場合:

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

周りにインスタンスがある場合:

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

オブジェクトからSwiftクラスの名前を取得するには(例:varオブジェクト:SomeClass()の場合)、

String(describing: type(of: object))

SomeClassなどのクラスタイプからSwiftクラスの名前を取得するには、以下を使用します。

String(describing: SomeClass.self)

出力:

「SomeClass」


13

あなたはこの方法を試すことができます:

self.classForCoder.description()

完全な「名前空間」(適切なターゲットメンバーシッププレフィックス)も含まれているため、これはうまく機能します。
BonanzaDriver 2016

ところで、「String(self)」を使用していることもわかりました。ここで、selfはMyViewControllerのインスタンスでもあり、同じように動作します... Xcode 7.1。
BonanzaDriver 2016

12

次の_stdlib_getDemangledTypeNameようなSwift標準ライブラリ関数を使用できます。

let name = _stdlib_getDemangledTypeName(myViewController)

@NeilJaff「単なるオプションの文字列」とはどういう意味ですか?文字列を返します。これはオプションではありません。
Jernej Strasner 2015年

3
このアプローチでは、プライベートAPI FYIのクラスの名前は返されません。最初のパブリック基本クラス名の名前を返します。
Tylerc230 2015

「未解決の識別子「_stdlib_getDemangledTypeName」の使用
Adobels

12

Swift 4で型名を文字列として取得するには(以前のバージョンを確認していません)、文字列補間を使用します。

"\(type(of: myViewController))"

.selfタイプ自体とtype(of:_)インスタンスの関数で使用できます。

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

8

ミラーを使用することもできます:

let vc = UIViewController()

String(Mirror(reflecting: vc).subjectType)

注意:このメソッドはStructsとEnumsにも使用できます。構造のタイプを示すdisplayStyleがあります。

Mirror(reflecting: vc).displayStyle

戻り値は列挙型なので、次のことができます。

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

ありがとう。それが私にとってうまくいきました。唯一のことは(少なくともiOS 9では)、subjectTypeが「.Type」で終わっているため、その部分を文字列から削除する必要がありました。
Nikolay Suvandzhiev 2016

8

Swift 5.1

ただし、クラス、構造体、列挙型、プロトコル、NSObjectの名前を取得できますSelf.self

print("\(Self.self)")

これはモジュール名の前に追加される場合があることに注意してください(とは異なりますString(describing: type(of: self))
Ixx

7

Swift 3.0: このような拡張を作成できます。プロジェクト名なしでクラス名が返されます。

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

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

これを使用しようとすると、次のエラーが発生します。タイプ「ProjectName.MyViewController」(0x1020409e8)の値を「Swift.AnyObject.Type」(0x7fb96fc0dec0)にキャストできませんでした。
tylerSF 2017

6

あなたはNSObjectProtocolこのようにSwift 4で拡張することができます:

import Foundation

extension NSObjectProtocol {

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

これにより、計算された変数classNameをすべてのクラスで使用できるようになります。これをprint()in 内で使用CalendarViewControllerすると"CalendarViewController"、コンソールに出力されます。


4

私はこの答えをしばらくの間探していました。私はGKStateMachineを使用して状態の変化を観察し、クラス名だけを確認する簡単な方法を望んでいました。iOS 10だけなのかSwift 2.3だけなのかはわかりませんが、その環境では、次のようにすれば私が望んでいることを正確に実行できます。

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

// Output:    
// Class Name: GKState


3

クラス名を文字列として取得するには、次のようにクラスを宣言します

@objc(YourClassName) class YourClassName{}

そして、次の構文を使用してクラス名を取得します

NSStringFromClass(YourClassName)

3

reflect().summaryクラスselfまたはインスタンスdynamicTypeを試してください。dynamicTypeを取得する前にオプションをアンラップします。それ以外の場合、dynamicTypeはオプションのラッパーです。

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;

概要は、一般的なタイプが記述されたクラスパスです。概要から単純なクラス名を取得するには、このメソッドを試してください。

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

上記の解決策は私にとってはうまくいきませんでした。主に生成された問題は、いくつかのコメントで言及されています。

MyAppName.ClassName

または

MyFrameWorkName.ClassName

このソリューションはXCode 9、Swift 3.0で機能しました。

classNameCleanedアクセスしやすく、将来のclassName()変更と競合しないように名前を付けました。

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

}

}

使用法:

NSViewController.classNameCleaned
MyCustomClass.classNameCleaned

2

Swift 3.0(macOS 10.10以降)、classNameから取得できます

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

1
私が知る限り、Swiftクラスには組み込みのclassNameプロパティはありません。何かからサブクラス化しましたか?
シム2016

@shim classNameはFoundationで宣言されていますが、macOS 10.10以降でのみ使用できるようです。回答を編集しました。
ThanhVũTrần16年

1
うーん、それは変だ。iOSに含まれないのはなぜですか?また、NSObject developer.apple.com
shim

2

type(of:...)プレイグラウンドでSwift 3 を試してみました。これが私の結果です。 これはコード形式のバージョンです。

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

クラス名を特定するためだけにインスタンスをインスタンス化するのは無駄です。
sethfri 2017年

@sethfriこれを使用して動的タイプを決定します。
Lumialxk

クラスの型情報を取得するためだけにインスタンスを作成する必要がある理由はまだわかりません
sethfri



1

マングルされた名前が気に入らない場合は、自分の名前を口述できます。

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

ただし、マングルされた名前を解析する方法を学ぶことは、長期的にはより良いでしょう。形式は標準的で意味があり、変更されません。


いいえ、そうではありません。私のクラスでは(ExistentialMetatype)が返されます-ドキュメント化されていないランタイム動作に依存することは常に危険です。
superarts.org 2014

1

時々、他のソリューションは、あなたが見ようとしているオブジェクトに依存して、役に立たない名前を与えるでしょう。その場合、以下を使用してクラス名を文字列として取得できます。

String(cString: object_getClassName(Any!))

x xcodeで関数をクリックして、かなり便利ないくつかの関連メソッドを表示します。またはこちらを確認してくださいhttps://developer.apple.com/reference/objectivec/objective_c_functions


1

Swift 5 クラス名を文字列として取得できます(説明:Self.self)

print(String(describing: Self.self))

0

私はSwift 2.2で使用しています

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

0

Swift 5.1:-

オブジェクトのクラス名を文字列として取得するためのジェネリック関数を使用することもできます

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

}

以下を使用してこの関数を呼び出します:-

let name = GenericFunctions.className(ViewController.self)

ハッピーコーディング:)


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.