代码之家  ›  专栏  ›  技术社区  ›  aunnnn

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

  •  12
  • aunnnn  · 技术社区  · 6 年前

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

    class MyView: UIView {
    
        init(custom: String) {
            super.init(frame: .zero)
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    }
    

    如我所料 不能 呼叫 MyView(frame: .zero) 因为它不是 automatically inherited 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 
            }
        }
    }
    

    这里我先检查了自定义视图 V init(frame:) 或者不,如果是的话,那就叫它。

    但是,它不起作用, Builder<MyView>().build() 将与以下对象碰撞:

    致命错误:对类“ulldb_expr_14.myview”使用了未实现的初始值设定项“init(frame:)”

    V.instancesRespond(to: #selector(V.init(frame:))) 总是返回 true ,使此支票无效。(或者我用错了吗?)

    问题:如何检查通用视图类 V 实际响应 初始化(帧:) ?


    更新1: V.responds(to: #selector(V.init(frame:))) false MyView

    framework

    enum ViewBuildMethod {
        case nib
        case nibName(String)
        case frame(CGRect)
        case custom(() -> UIView)
    }
    

    protocol SomeViewProtocol where Self: UIView {
    
        // ... other funcs
    
        static func buildMethod() -> ViewBuildMethod
    }
    

    override init(frame:) fatalError buildMethod .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")
        //     })
        // }
    }
    

    V.init(frame:) is called indirectly but V doesn't override this designated initializer. Please override init(frame:) to fix this issue

    3 回复  |  直到 6 年前
        1
  •  6
  •   Taras Chernyshenko    6 年前

    UIView init(frame:)

    instancesRespond(to:) true

    protocol

    Buildable init(frame: CGRect)

    protocol Buildable {
        init(frame: CGRect)
    }
    

    class MyView: UIView, Buildable {...}
    

    Builder

    class Builder<V: Buildable>  {
        func build() -> V? {
            return V(frame: .zero)
        }
    }
    

    Builder<MyView>().build() compile-time

    class SecondView: UIView {
        init(custom: String) {
            super.init(frame: .zero)
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
        }
    }
    
    Builder<SecondView>().build()
    

    error: type 'SecondView' does not conform to protocol 'Buildable'
    Builder<SecondView>().build()
    
        2
  •  1
  •   sang    6 年前

        3
  •  1
  •   Cristik    6 年前

    init(frame:)

    var methodCount: UInt32 = 0
    let methods = class_copyMethodList(MyView.self, &methodCount)!      
    for i in 0..<methodCount {
        let method = method_getName(methods[Int(i)])
        print(method, method_getImplementation(methods[Int(i)]))
    }
    let method = class_getMethodImplementation(UIView.self,  #selector(UIView.init(frame:)))!
    print("UIView's init(frame:)",method_getImplementation(method))
    

    initWithCoder: 0x00000001054125b0
    initWithFrame: 0x0000000105412790
    UIView's init(frame:) 0xffee1be8ffffee20
    

    UIView