如何检查通用视图类是否实际实现了init(frame :)?

aun*_*nnn 12 generics ios swift

假设我有一个带有指定初始值设定项的自定义UIView子类:

class MyView: UIView {

    init(custom: String) {
        super.init(frame: .zero)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}
Run Code Online (Sandbox Code Playgroud)

正如预期的那样我不能打电话MyView(frame: .zero),因为它是不会自动继承UIView.

然后假设我有一个视图构建器类:

class Builder<V: UIView> {
    func build() -> V? {
        // Check if can call init(frame:) or not
        if V.instancesRespond(to: #selector(V.init(frame:))) {
            return V.init(frame: .zero) // <-- Crash here, because the check doesn't work
        } else { 
            return nil 
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在这里我首先检查自定义视图V是否有init(frame:),如果是,则调用它.

但是,它不起作用,Builder<MyView>().build()会崩溃:

致命错误:对类'__lldb_expr_14.MyView'使用未实现的初始化程序'init(frame :)'

V.instancesRespond(to: #selector(V.init(frame:)))总是返回true,使这个检查无用.(或者我错误地使用了它?)

问题:如何检查通用视图类是否V实际响应init(frame:)


更新1: 我也尝试V.responds(to: #selector(V.init(frame:)))过@kamaldeep和@Pranav指出.但是,false无论我是否重写init(frame :) ,它总是返回MyView.

更新2:我正在尝试做什么:

为了更清楚,我正在构建一个自动从这个枚举初始化的框架UIView:

enum ViewBuildMethod {
    case nib
    case nibName(String)
    case frame(CGRect)
    case custom(() -> UIView)
}
Run Code Online (Sandbox Code Playgroud)

并且要与框架一起使用的视图必须采用此协议并指定如何构建:

protocol SomeViewProtocol where Self: UIView {

    // ... other funcs

    static func buildMethod() -> ViewBuildMethod
}
Run Code Online (Sandbox Code Playgroud)

问题是我希望它override init(frame:)可选的,并允许自定义指定的初始化程序(如in MyView).然后,fatalErrorinit(frame:)(未间接)在尚未覆盖它的视图上使用(间接)时,发出错误消息.这表明非法使用框架(如MyViewbuildMethod回报.frame(.zero)),不能在编译时检查:

class MyView: UIView, SomeViewProtocol {

    init(custom: String) {
        super.init(frame: .zero)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    // ILLEGAL!!
    static func buildMethod() -> ViewBulidMethod {
        return .frame(.zero) // <-- Illegal, the framework will call V.init(frame: .zero) eventually
    }

    // LEGAL
    // static func buildMethod() -> ViewBuildMethod {
    //     return .custom({ () -> UIView in
    //         return MyView(custom: "hello")
    //     })
    // }
}
Run Code Online (Sandbox Code Playgroud)

错误消息可能是这样的V.init(frame:) is called indirectly but V doesn't override this designated initializer. Please override init(frame:) to fix this issue.我可以像上面那样让它崩溃,但是如果我能在这之前添加一些有意义的消息,那就更清楚了.

Tar*_*nko 6

由于Swift不会自动从中继承,UIView因此无法检查您的类是否实现了init(frame:)构造函数.

我想instancesRespond(to:)回来true,因为它正在检查你的类是否符合消息调度的这条消息.但是,Swift对类中声明的方法使用Table调度.因此,此检查将与Objective-C一起使用,但不适用于Swift

所以,要实现你想要的,你可以使用 protocol

创建Buildable具有init(frame: CGRect)方法的协议

protocol Buildable {
    init(frame: CGRect)
}
Run Code Online (Sandbox Code Playgroud)

使您的班级符合此协议

class MyView: UIView, Buildable {...}
Run Code Online (Sandbox Code Playgroud)

现在init(frame:)你的班级需要方法.将您的Builder类的泛型类型更改为Buildable

现在你的课可能是这样的

class Builder<V: Buildable>  {
    func build() -> V? {
        return V(frame: .zero)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,当你能够构建你的视图时Builder<MyView>().build(),如果你的课程不能确认Buildable协议,你将收到compile-time错误:

class SecondView: UIView {
    init(custom: String) {
        super.init(frame: .zero)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

Builder<SecondView>().build()
Run Code Online (Sandbox Code Playgroud)

将抛出编译时错误:

error: type 'SecondView' does not conform to protocol 'Buildable'
Builder<SecondView>().build()
Run Code Online (Sandbox Code Playgroud)