Làm thế nào để bạn tìm ra loại đối tượng (trong Swift)?


255

Khi cố gắng hiểu một chương trình, hoặc trong một số trường hợp góc, thật hữu ích để có thể thực sự tìm ra loại gì đó. Tôi biết trình gỡ lỗi có thể hiển thị cho bạn một số thông tin loại và bạn thường có thể dựa vào suy luận kiểu để thoát khỏi việc không chỉ định loại trong các tình huống đó, tuy nhiên, tôi thực sự muốn có một cái gì đó giống như của Pythontype()

DynamicType (xem câu hỏi này )

Cập nhật: điều này đã được thay đổi trong một phiên bản gần đây của Swift, obj.dynamicTypegiờ đây cung cấp cho bạn một tham chiếu đến loại và không phải là thể hiện của loại động.

Điều này có vẻ hứa hẹn nhất, nhưng cho đến nay tôi vẫn chưa thể tìm ra loại thực tế

class MyClass {
    var count = 0
}

let mc = MyClass()

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

Tôi cũng đã cố gắng sử dụng một tham chiếu lớp để tạo một đối tượng mới, mà không làm việc, nhưng kỳ quặc đã cho tôi một lỗi nói rằng tôi phải thêm một requiredinitializer:

làm:

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

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

Vẫn chỉ là một bước nhỏ để thực sự khám phá loại của bất kỳ đối tượng nào.

chỉnh sửa : Tôi đã xóa một số lượng đáng kể các chi tiết không liên quan - hãy xem lịch sử chỉnh sửa nếu bạn quan tâm :)



1
Thật thú vị, print(mc)hoặc dump(mc)sẽ in một bản tóm tắt (mà bạn có thể nhận được từ toString(mc)hoặc reflect(mc).summary), trong đó sẽ chứa tên lớp ở đó ở đâu đó. Nhưng không rõ làm thế nào để có được tên lớp.
newacct

@David tương tự, nhưng không phải tất cả các biến là các thể hiện của lớp. Ngoài ra, các câu hỏi thực sự là về việc kiểm tra xem loại đó có khớp với những gì lập trình viên đang tìm kiếm hay không, trong khi tôi hy vọng sẽ tìm ra loại bán buôn
Jiaaro


Câu trả lời:


284

Phiên bản Swift 3:

type(of: yourObject)

8
Sự thật thú vị. Điều này không hoạt động với các tùy chọn ngầm định! tức var myVar: SomeType!. Trình biên dịch đưa ra lỗi "Không thể chuyển đổi giá trị của loại 'Một số loại! .Type' (còn gọi là 'ImplicitlyUnwrappingOptional <someType> .Type') sang loại đối số dự kiến ​​'AnyClass' (còn gọi là 'AnyObject.Type') Trình biên dịch đề xuất thêm as! AnyClasssau loại nhưng sau đó chương trình gặp sự cố với một số "EXC_BAD_INXD" và jiberrish khác mà tôi không thể giải mã.
LightningStryk

1
Thật vậy, đây phải là câu trả lời được chấp nhận ngay bây giờ khi Swift 3 tồn tại. Cảm ơn Jeremy!
biomiker

1
Nếu bạn đang tìm kiếm tên loại cụ thể, khi loại đó là loại giao thức, điều này có thể không phù hợp với bạn.
Hoàng tử Chris

2
Nếu bạn có một Stringcái được truyền dưới dạng Anythì type(of:)sẽ xuất ra Any, không String.
ScottyBlades

1
@ScottyBlades vậy giải pháp sẽ là gì. Bạn có thể cung cấp?
Trung tâm thương mại Mubin

109

Swift 2.0:

Cách thích hợp để thực hiện kiểu hướng nội này là với cấu trúc Gương ,

    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)

Sau đó, để truy cập loại từ Mirrorcấu trúc, bạn sẽ sử dụng thuộc tính subjectTypenhư vậy:

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

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

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

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

Sau đó, bạn có thể sử dụng một cái gì đó như thế này:

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

7
Điều đó thật tuyệt. Xin lưu ý rằng nếu đối tượng được nhân đôi là loại tùy chọn, thì việc so sánh nó với loại không tùy chọn sẽ thất bại. StringOptional(String)không giống nhau
Thomas Verbeek

1
Chính xác những gì tôi đang tìm kiếm, muốn biết loại vật thể này là gì
Joseph

Có một loại so sánh trong bối cảnh này sẽ không thất bại khi so sánh tùy chọn với các loại không tùy chọn?
Hoàng tử Chris

Đó là những gì tôi đang tìm kiếm. Cảm ơn bạn @Gudbergur.
Trung tâm thương mại Mubin

60

Các dynamicType.printClassNamecode đang từ một ví dụ trong cuốn sách Swift. Không có cách nào tôi biết để lấy trực tiếp một tên lớp tùy chỉnh, nhưng bạn có thể kiểm tra một loại thể hiện bằng cách sử dụng istừ khóa như dưới đây. Ví dụ này cũng cho thấy cách triển khai hàm className tùy chỉnh, nếu bạn thực sự muốn tên lớp dưới dạng chuỗi.

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

Lưu ý:
các lớp con NSObjectđã thực hiện chức năng className của riêng chúng. Nếu bạn đang làm việc với Ca cao, bạn chỉ có thể sử dụng tài sản này.

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

2
Này, không chắc chắn khi nó thay đổi, nhưng như Alex Pretzlav đã chỉ ra, hành vi đã thay đổi.
Jiaaro

1
Đúng. Kể từ Swift 3.0, subjectTypekhông còn khả dụng và dynamicTypegây ra thông báo phản đối từ trình biên dịch.
Raphael

41

Kể từ Xcode 6.0.1 (ít nhất, không chắc chắn khi họ thêm nó), ví dụ ban đầu của bạn hiện hoạt động:

class MyClass {
    var count = 0
}

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

Cập nhật:

Để trả lời câu hỏi ban đầu, bạn thực sự có thể sử dụng thời gian chạy Objective-C với các đối tượng Swift đơn giản thành công.

Hãy thử như sau:

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

Có vẻ như họ đã thay đổi nó để cung cấp cho bạn loại thay vì một thể hiện.
Jiaaro

@Jiaaro, tôi đã cập nhật câu trả lời của mình với những gì tôi nghĩ rằng bạn đang tìm kiếm trong câu hỏi ban đầu của bạn
Alex Pretzlav

36

Nếu bạn chỉ cần kiểm tra xem biến đó có phải là loại X hay nó có phù hợp với một số giao thức hay không, thì bạn có thể sử dụng is, hoặc as?như sau:

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

Điều này tương đương với isKindOfClasstrong Obj-C.

Và điều này tương đương với conformsToProtocol, hoặcisMemberOfClass

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

Phần thứ hai của câu trả lời của bạn là sai. Câu lệnh 'if let' với các as?diễn viên có điều kiện cũng làm isKindOfClassnhư vậy, cũng cung cấp kết quả của các diễn viên nên thành công.
awolf 11/03/2016

Tương đương isMemberOfClasslà điều kiện object.dynamicType == ClassName.self.
awolf 11/03/2016

18

Swift 3:

if unknownType is MyClass {
   //unknownType is of class type MyClass
}

Tôi nghĩ iscó tồn tại từ trước Swift 3 ...?
Nicolas Miari

10

Dành cho Swift 3.0

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

Dành cho Swift 2.0 - 2.3

String(<Class-Name>)

1
Điều quan trọng về câu trả lời này là chính xác đối với tôi, là chuỗi kết quả khớp chính xác với tên lớp - vì vậy tôi có thể sử dụng điều này để lấy tên thực thể Dữ liệu lõi từ một lớp con NSManagedObject. Tôi đã sử dụng phiên bản Swift3.
Kendall Helmstetter Gelner

8

Đây là 2 cách tôi khuyên bạn nên làm:

if let thisShape = aShape as? Square 

Hoặc là:

aShape.isKindOfClass(Square)

Dưới đây là một ví dụ chi tiết:

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 ), isnhà điều hành là thích hợp hơn.
DawnSong

Giải pháp tốt cho tôi để có được các loại đối tượng.
nihasmata

1

Phụ thuộc vào trường hợp sử dụng. Nhưng hãy giả sử bạn muốn làm một cái gì đó hữu ích với các loại "biến" của bạn. switchTuyên bố Swift rất mạnh mẽ và có thể giúp bạn nhận được kết quả mà bạn đang tìm kiếm ...

    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
    }

Trong trường hợp này, có một từ điển đơn giản chứa các cặp khóa / giá trị có thể là UInt, Int hoặc String. Trong .filter()phương thức trên từ điển, tôi cần đảm bảo rằng tôi kiểm tra các giá trị một cách chính xác và chỉ kiểm tra một Chuỗi khi đó là một chuỗi, v.v ... Câu lệnh chuyển đổi làm cho việc này trở nên đơn giản và an toàn! Bằng cách gán 9 cho biến kiểu Any, nó làm cho chuyển đổi cho Int thực thi. Hãy thử thay đổi nó thành:

   let eValue:Any = "home9"

.. và thử lại lần nữa. Lần này nó thực hiện as Stringvụ án.


1

Nếu bạn nhận được cảnh báo "luôn luôn đúng / không thành công", bạn có thể cần phải chuyển sang Bất kỳ trước khi sử dụng 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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.