Tìm hiểu nội quan & khái quát lớp Swift


121

Tôi đang cố gắng tạo động một classkiểu dựa trên phiên bản bằng cách sử dụng generic, tuy nhiên tôi đang gặp khó khăn với việc xem xét nội dung lớp.

Đây là những câu hỏi:

  • Có Swift tương đương với Obj-C self.classkhông?
  • Có cách nào để khởi tạo một lớp bằng cách sử dụng AnyClasskết quả từ NSClassFromStringkhông?
  • Có cách nào để lấy AnyClasshoặc nhập thông tin từ một tham số chung Tkhông? (Tương tự với typeof(T)cú pháp của C # )

2
stackoverflow.com/a/24069875/292145 cung cấp một số gợi ý về API phản chiếu Swift.
Klaas

5
Objective-C self.classsẽ trở thành self.dynamicType.selftrong Swift tôi niềm tin
Filip Hermans

1
Trong một phương thức phiên bản, đây là cách gọi một phương thức lớp:self.dynamicType.foo()

Câu trả lời:


109

Vâng, đối với một, Swift tương đương [NSString class].self(xem tài liệu Metatype , mặc dù chúng khá mỏng).

Trong thực tế, NSString.classthậm chí không hoạt động! Bạn phải sử dụng NSString.self.

let s = NSString.self
var str = s()
str = "asdf"

Tương tự, với một lớp học nhanh, tôi đã thử ...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

Hmm… lỗi cho biết:

Thực thi sân chơi không thành công: error:: 16: 1: error: xây dựng một đối tượng thuộc loại lớp 'X' với giá trị metatype yêu cầu trình khởi tạo '@required'

 Y().me()
 ^
 <REPL>:3:7: note: selected implicit initializer with type '()'
 class X {
       ^

Tôi đã mất một lúc để hiểu điều này có nghĩa là gì… hóa ra nó muốn lớp có một @required init()

class X {
    func me() {
        println("asdf")
    }

    required init () {

    }
}

let Y = X.self

// prints "asdf"
Y().me()

Một số tài liệu đề cập đến điều này là .Type, nhưng MyClass.Typecho tôi một lỗi trong sân chơi.


1
Cảm ơn bạn đã liên kết đến tài liệu Metatype! Tôi hoàn toàn bỏ qua khía cạnh đó của Loại, doh!
Erik

14
Bạn có thể sử dụng .Typehoặc .Protocoltrong khai báo biến, ví dụlet myObject: MyObject.Type = MyObject.self
Sulthan

1
Sulthan: vì vậy MyObject.Type là một khai báo nhưng MyObject.self là một phương thức nhà máy (có thể được gọi) và myObject là một biến chứa tham chiếu đến một phương thức nhà máy. Lời gọi myObject () sẽ tạo ra một thể hiện của lớp MyObject. Sẽ là ví dụ tốt hơn nếu tên của biến myObject là myObjectFactory?
bootchk

2
@trước requirednên bị xóa
fujianjin6471

49

Đây là cách sử dụng NSClassFromString. Bạn phải biết siêu lớp của những gì bạn sắp kết thúc. Đây là một cặp lớp cha-lớp con biết cách mô tả chính chúng cho println:

@objc(Zilk) class Zilk : NSObject {
    override var description : String {return "I am a Zilk"}
}

@objc(Zork) class Zork : Zilk {
    override var description : String {return "I am a Zork"}
}

Lưu ý việc sử dụng @objcú pháp đặc biệt để xác định tên hỗn hợp Objective-C của các lớp này; điều đó rất quan trọng, bởi vì nếu không chúng ta không biết chuỗi munged chỉ định mỗi lớp.

Bây giờ chúng ta có thể sử dụng NSClassFromStringđể tạo lớp Zork hoặc lớp Zilk, bởi vì chúng ta biết rằng chúng ta có thể nhập nó dưới dạng NSObject và không bị lỗi sau này:

let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"

Và nó có thể đảo ngược; println(NSStringFromClass(anObject.dynamicType))cũng hoạt động.


Phiên bản hiện đại:

    if let aClass = NSClassFromString("Zork") as? NSObject.Type {
        let anObject = aClass.init()
        print(anObject) // "I am a Zork"
        print(NSStringFromClass(type(of:anObject))) // Zork
    }

10
Ủng hộ cho @objc(ClassName)bit. Tôi biết về @objcthuộc tính nhưng không phải bạn cũng có thể đưa ra gợi ý về tên lớp.
Erik

1
Giải pháp tuyệt vời mà vẫn hoạt động ít nhiều như đã viết 6 năm sau. Chỉ một vài chỉnh sửa nhỏ mà sân chơi yêu cầu: as! NSObject.Typeở dòng đầu tiên và dòng aClass.init()thứ hai
Kaji

13

Nếu tôi đang đọc đúng tài liệu, nếu bạn xử lý các trường hợp và ví dụ: muốn trả về một phiên bản mới của cùng Loại với đối tượng bạn đã được cung cấp và Loại có thể được xây dựng bằng init (), bạn có thể làm:

let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()

Tôi nhanh chóng kiểm tra nó với String:

let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")

hoạt động tốt.


1
Đúng, dynamicTypehoạt động như tôi mong đợi ở đó. Tuy nhiên, tôi đã không thể so sánh các loại. Công dụng lớn thực sự là với thuốc generic, vì vậy tôi có thể có một cái gì đó giống Generic<T>và bên trong if T is Double {...}. Có vẻ như điều đó không thể xảy ra một cách đáng tiếc.
Erik

1
@SiLo Bạn đã bao giờ tìm cách hỏi chung liệu hai đối tượng có thuộc cùng một lớp không?
matt

1
@matt Không thanh lịch, không, tôi không. Tuy nhiên, tôi đã có thể tạo một Defaultablegiao thức hoạt động tương tự như defaulttừ khóa của C # và các phần mở rộng thích hợp cho các loại như StringInt. Bằng cách thêm ràng buộc chung của T:Defaultable, tôi có thể kiểm tra xem đối số có được truyền hay không is T.default().
Erik

1
@SiLo Thông minh; Tôi muốn xem mã đó! Tôi hiểu rằng điều này giải quyết được những hạn chế kỳ lạ trong việc sử dụng "is". Tôi đã đưa ra một lỗi về những hạn chế đó và cả về sự thiếu hiểu biết chung của lớp. Tôi đã kết thúc việc so sánh các chuỗi bằng cách sử dụng NSStringFromClass, nhưng tất nhiên điều đó chỉ hoạt động cho các con cháu của NSObject.
matt

1
@matt Thật không may, nó nghe có vẻ thông minh hơn thực tế vì bạn vẫn phải làm value is String.default()... vv, mà thay vào đó bạn sẽ làm value is String.
Erik

13

Trong 3 nhanh

object.dynamicType

không được dùng nữa.

Thay vào đó hãy sử dụng:

type(of:object)

7

Triển khai nhanh các kiểu so sánh

protocol Decoratable{}
class A:Decoratable{}
class B:Decoratable{}
let object:AnyObject = A()
object.dynamicType is A.Type//true
object.dynamicType is B.Type//false
object.dynamicType is Decoratable.Type//true

LƯU Ý: Lưu ý rằng nó cũng hoạt động với các giao thức mà đối tượng có thể mở rộng hoặc không


1

Cuối cùng cũng có thứ để làm việc. Nó hơi lười biếng nhưng ngay cả tuyến NSClassFromString () cũng không hoạt động với tôi ...

import Foundation

var classMap = Dictionary<String, AnyObject>()

func mapClass(name: String, constructor: AnyObject) -> ()
{
    classMap[name] = constructor;
}

class Factory
{
    class func create(className: String) -> AnyObject?
    {
        var something : AnyObject?

        var template : FactoryObject? = classMap[className] as? FactoryObject

        if (template)
        {
            let somethingElse : FactoryObject = template!.dynamicType()

            return somethingElse
        }

        return nil
    }
}


 import ObjectiveC

 class FactoryObject : NSObject
{
    @required init() {}
//...
}

class Foo : FactoryObject
{
    class override func initialize()
    {
        mapClass("LocalData", LocalData())
    }
    init () { super.init() }
}

var makeFoo : AnyObject? = Factory.create("Foo")

và bingo, "makeFoo" chứa một phiên bản Foo.

Nhược điểm là các lớp của bạn phải khởi động từ FactoryObject và chúng PHẢI có phương thức khởi tạo obj-C + để lớp của bạn được tự động chèn vào bản đồ lớp bằng hàm toàn cầu "mapClass".


1

Dưới đây là một ví dụ khác cho thấy việc triển khai phân cấp lớp, tương tự như câu trả lời được chấp nhận, được cập nhật cho bản phát hành đầu tiên của Swift.

class NamedItem : NSObject {
    func display() {
        println("display")
    }

    required override init() {
        super.init()
        println("base")
    }
}

class File : NamedItem {
    required init() {
        super.init()
        println("folder")
    }
}

class Folder : NamedItem {
    required init() {
        super.init()
        println("file")
    }
}

let y = Folder.self
y().display()
let z = File.self
z().display()

In kết quả này:

base
file
display
base
folder
display

2
Kỹ thuật này không hoạt động chính xác nếu biến là Kiểu của lớp cha. Ví dụ, đã cho var x: NamedItem.Type, nếu tôi gán nó x = Folder.Type, thì x()trả về một mới NamedItem, không phải a Folder. Điều này làm cho kỹ thuật trở nên vô dụng đối với nhiều ứng dụng. Tôi coi đây là một lỗi .
phatmann

1
Trên thực tế, bạn có thể làm những gì tôi nghĩ bạn muốn bằng cách sử dụng kỹ thuật này stackoverflow.com/questions/26290469/…
Possen
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.