คุณจะรู้ประเภทของวัตถุได้อย่างไร (ใน Swift)


255

เมื่อพยายามที่จะเข้าใจโปรแกรมหรือในบางมุมมันมีประโยชน์ที่จะสามารถรู้ได้ว่าจริงๆแล้วมันคืออะไร ฉันรู้ว่าตัวดีบั๊กสามารถแสดงข้อมูลบางอย่างให้คุณได้และคุณสามารถพึ่งพาการอนุมานเพื่อหลีกเลี่ยงการไม่ระบุประเภทในสถานการณ์เหล่านั้น แต่ถึงกระนั้นฉันก็อยากมีบางอย่างเหมือน Pythontype()

dynamicType (ดูคำถามนี้ )

อัปเดต:สิ่งนี้มีการเปลี่ยนแปลงใน Swift เวอร์ชันล่าสุดobj.dynamicTypeตอนนี้ให้การอ้างอิงถึงประเภทและไม่ใช่อินสแตนซ์ของประเภทไดนามิก

อันนี้ดูเหมือนจะมีแนวโน้มมากที่สุด แต่จนถึงขณะนี้ฉันยังไม่สามารถค้นหาชนิดที่แท้จริงได้

class MyClass {
    var count = 0
}

let mc = MyClass()

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

ฉันยังลองใช้การอ้างอิงคลาสเพื่อสร้างอินสแตนซ์ของวัตถุใหม่ซึ่งใช้งานได้ แต่มีข้อผิดพลาดเกิดขึ้นกับฉันว่าฉันต้องเพิ่มrequiredinitializer:

การทำงาน:

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

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

ยังเป็นเพียงก้าวเล็ก ๆ ที่ไปสู่การค้นพบชนิดของวัตถุใด ๆ ก็ตาม

แก้ไข : ฉันได้ลบรายละเอียดที่ไม่เกี่ยวข้องจำนวนมากตอนนี้ - ดูประวัติการแก้ไขหากคุณสนใจ :)



1
ที่น่าสนใจprint(mc)หรือdump(mc)จะพิมพ์บทสรุป (ซึ่งคุณสามารถได้รับจากtoString(mc)หรือreflect(mc).summary) ซึ่งจะมีชื่อชั้นในนั้น แต่ยังไม่ชัดเจนว่าจะได้รับเพียงแค่ชื่อชั้นเรียนด้วยตัวเอง
newacct

@David คล้ายกัน แต่ไม่ใช่ตัวแปรทั้งหมดเป็นอินสแตนซ์ของคลาส นอกจากนี้คำถามนั้นเกี่ยวกับการตรวจสอบว่าประเภทตรงกับสิ่งที่โปรแกรมเมอร์กำลังมองหาหรือไม่ในขณะที่ฉันหวังว่าจะได้พบกับประเภทขายส่ง
Jiaaro


คำตอบ:


284

เวอร์ชั่น Swift 3:

type(of: yourObject)

8
สนุกจริงๆ. สิ่งนี้ใช้ไม่ได้กับตัวเลือกที่ยังไม่ได้เปิดโดยปริยาย! var myVar: SomeType!กล่าวคือ คอมไพเลอร์ให้ข้อผิดพลาด "ไม่สามารถแปลงค่าของประเภท 'SomeType! .Type' (aka 'ImplicitlyUnwrappedOptional <SomeType> .Type') เป็นประเภทอาร์กิวเมนต์ที่คาดหวัง 'AnyClass' (aka 'AnyObject.Type') คอมไพเลอร์แนะนำให้เพิ่มas! AnyClassหลังจากชนิด แต่แล้ว โปรแกรมขัดข้องด้วย "EXC_BAD_INSTRUCTION" และ jiberrish อื่น ๆ ที่ฉันไม่สามารถถอดรหัสได้
LightningStryk

1
อันที่จริงควรเป็นคำตอบที่ได้รับการยอมรับในขณะนี้ว่า Swift 3 มีอยู่ ขอบคุณ Jeremy!
biomiker

1
หากคุณกำลังมองหาชื่อประเภทเฉพาะเมื่อประเภทนั้นเป็นประเภทโปรโตคอลสิ่งนี้อาจไม่เหมาะกับคุณ
คริสเจ้าชาย

2
หากคุณมีStringที่ถูกส่งผ่านเป็นชนิดAnyแล้วtype(of:)จะออกไม่ได้Any String
ScottyBlades

1
@ScottyBlades ดังนั้นสิ่งที่จะเป็นทางออก คุณสามารถให้
Mubin Mall

109

Swift 2.0:

วิธีที่เหมาะสมในการทำชนิดของประเภทวิปัสสนานี้จะเป็นกับstruct กระจก ,

    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)

จากนั้นในการเข้าถึงชนิดของตัวเองจากMirrorstruct คุณจะต้องใช้คุณสมบัติsubjectTypeเช่น:

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

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

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

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

จากนั้นคุณสามารถใช้สิ่งนี้:

    if anyMirror.subjectType == String.self {
        print("anyObject is a string!")
    } else {
        print("anyObject is not a string!")
    }

7
มันเยี่ยมมาก ระวังว่าถ้าวัตถุที่ถูกมิร์เรอร์เป็นชนิดที่เลือกระบุได้การเปรียบเทียบกับประเภทที่ไม่ใช่ตัวเลือกจะล้มเหลว StringและOptional(String)ไม่เหมือนกัน
Thomas Verbeek

1
สิ่งที่ฉันกำลังมองหาอยากรู้ว่าประเภทของวัตถุคืออะไร
Joseph

มีการเปรียบเทียบประเภทหนึ่งในบริบทนี้ที่จะไม่ล้มเหลวเมื่อเปรียบเทียบทางเลือกกับประเภทที่ไม่จำเป็นหรือไม่
Chris Prince

นั่นคือสิ่งที่ฉันกำลังมองหา ขอบคุณ @Gudbergur
Mubin Mall

60

dynamicType.printClassNameรหัสคือจากตัวอย่างในหนังสือเล่ม Swift ฉันไม่มีทางรู้ว่าจะคว้าชื่อคลาสที่กำหนดเองได้โดยตรง แต่คุณสามารถตรวจสอบประเภทอินสแตนซ์โดยใช้isคำหลักดังที่แสดงด้านล่าง ตัวอย่างนี้ยังแสดงวิธีการใช้ฟังก์ชั่น className ที่กำหนดเองถ้าคุณต้องการชื่อชั้นเป็นสตริง

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

หมายเหตุ:
คลาสย่อยที่NSObjectมีการใช้งานฟังก์ชัน className ของตัวเองอยู่แล้ว หากคุณทำงานกับ Cocoa คุณสามารถใช้คุณสมบัตินี้ได้

class MyObj: NSObject {
    init() {
        super.init()
        println("My class is \(self.className)")
    }
}
MyObj()

2
เฮ้ไม่แน่ใจว่าเมื่อมันเปลี่ยนไป แต่เมื่ออเล็กซ์ Pretzlav ชี้ให้เห็นพฤติกรรมที่เปลี่ยนไป
Jiaaro

1
ใช่. ในฐานะของ Swift 3.0 subjectTypeไม่สามารถใช้งานได้อีกต่อไปและdynamicTypeทำให้ข้อความเลิกใช้งานจากคอมไพเลอร์
Raphael

41

ในฐานะของXcode 6.0.1 (อย่างน้อยไม่แน่ใจว่าเมื่อพวกเขาเพิ่มมัน) ตอนนี้ตัวอย่างดั้งเดิมของคุณใช้งานได้:

class MyClass {
    var count = 0
}

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

ปรับปรุง:

เพื่อตอบคำถามเดิมคุณสามารถใช้จริง Objective-C runtime กับวัตถุ Swift ธรรมดาได้สำเร็จ

ลองทำสิ่งต่อไปนี้:

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

ดูเหมือนว่าพวกเขาเปลี่ยนเป็นประเภทให้คุณแทนที่จะเป็นอินสแตนซ์
Jiaaro

@ เจียโร่, ฉันได้อัพเดตคำตอบของฉันด้วยสิ่งที่ฉันคิดว่าคุณกำลังมองหาในคำถามเดิมของคุณ
Alex Pretzlav

36

หากคุณเพียงแค่ต้องตรวจสอบว่าตัวแปรเป็นประเภท X หรือว่าสอดคล้องกับโปรโตคอลบางอย่างจากนั้นคุณสามารถใช้isหรือas?ดังต่อไปนี้:

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

นี่เทียบเท่าisKindOfClassใน Obj-C

และนี่เทียบเท่าconformsToProtocolหรือisMemberOfClass

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

ส่วนที่สองของคำตอบของคุณผิด ข้อความ 'if let' ที่มีas?เงื่อนไขการร่ายไม่เหมือนกันisKindOfClassเช่นกันเพียงแค่ให้ผลลัพธ์ของการร่ายหากสำเร็จ
awolf

เทียบเท่าเป็นเงื่อนไขisMemberOfClass object.dynamicType == ClassName.self
awolf


10

สำหรับ Swift 3.0

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

สำหรับ Swift 2.0 - 2.3

String(<Class-Name>)

1
สิ่งสำคัญเกี่ยวกับคำตอบนี้ถูกต้องสำหรับฉันคือสตริงผลลัพธ์ตรงกับชื่อคลาส - ดังนั้นฉันสามารถใช้สิ่งนี้เพื่อรับชื่อเอนทิตี Core Data จากคลาสย่อย NSManagedObject ฉันใช้รุ่น Swift3
Kendall Helmstetter Gelner

8

นี่คือ 2 วิธีที่ฉันแนะนำให้ทำ:

if let thisShape = aShape as? Square 

หรือ:

aShape.isKindOfClass(Square)

นี่คือตัวอย่างโดยละเอียด:

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

2
print( aShape is Square ), isผู้ประกอบการเป็นที่นิยมมากขึ้น
DawnSong

ทางออกที่ดีสำหรับฉันที่จะได้รับประเภทของวัตถุ
nihasmata

1

ขึ้นอยู่กับกรณีการใช้งาน แต่สมมติว่าคุณต้องการทำสิ่งที่มีประโยชน์กับประเภท "ตัวแปร" ของคุณ switchคำสั่งSwift มีประสิทธิภาพมากและสามารถช่วยให้คุณได้ผลลัพธ์ที่คุณต้องการ ...

    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
    }

ในกรณีนี้ให้พจนานุกรมง่าย ๆ ที่มีคู่ของคีย์ / ค่าที่สามารถเป็น UInt, Int หรือ String ใน.filter()วิธีการในพจนานุกรมฉันต้องตรวจสอบให้แน่ใจว่าฉันทดสอบค่าอย่างถูกต้องและทดสอบเฉพาะสตริงเมื่อเป็นสตริง ฯลฯ คำสั่งสวิตช์ทำให้สิ่งนี้ง่ายและปลอดภัย! ด้วยการกำหนด 9 ให้กับตัวแปรชนิดใด ๆ มันทำให้สวิตช์สำหรับการเรียกใช้งาน Int ลองเปลี่ยนเป็น:

   let eValue:Any = "home9"

.. และลองอีกครั้ง คราวนี้มันจะดำเนินการas Stringกรณี


1

หากคุณได้รับคำเตือน "เสมอจริง / ล้มเหลว" คุณอาจต้องส่งไปที่ใด ๆ ก่อนที่จะใช้ is

(foo as Any) is SomeClass

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.