是什么原因导致 UIBarButtonItem 出现此初始值设定项继承问题



>我正在尝试创建一个简单的 UIBarButtonItem 子类:

class LabelBarButtonItem: UIBarButtonItem {
  let label: UILabel
  init(text: String) {
    self.label = UILabel(frame: CGRectZero)
    self.label.tintColor = UIColor.grayColor()
    super.init(customView: self.label)
    self.label.text = text
  }
  required init(coder aDecoder: NSCoder) {
      fatalError("init(coder:) has not been implemented")
  }
}

但是当我尝试实例化它时:

let countButton = LabelBarButtonItem(text: "0 items")

代码正确编译,但在运行时失败,并显示:

fatal error: use of unimplemented initializer 'init()' for class 'TestProject.LabelBarButtonItem'

我不明白为什么在这种情况下会发生这种情况,或者通常是什么原则导致这个问题。init(text)初始值设定项应直接委托给超类中的init(customView)。为什么叫init()?它不应该参与其中。

谁能解释一下发生了什么?当我尝试对其他 UIKit 类进行子类化时,也出现了类似的问题

问题是init(customView:)调用init() .

而且由于您具有所需的初始值设定项,因此您不再继承init() .因此,您必须实现它,即使只是调用super.init()

然后,您将面临必须初始化init()init(text:)中的所有属性的(轻微)烦恼。但是,在您的特定情况下,首先不需要在初始值设定项中执行此操作。我会这样写你的类:

class LabelBarButtonItem: UIBarButtonItem {
    let label = UILabel(frame: CGRectZero)
    init(text: String) {
        super.init(customView: self.label)
        self.label.text = text
        self.label.tintColor = UIColor.grayColor()
    }
    private override init() {
        super.init()
    }
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

在您的评论中,您问:

但是现在如果我覆盖 init(),我的类的客户端可以通过调用 LabelBarButtonItem() 来创建 LabelBarButtonItem 的无效实例

正确。这就是为什么我将init()覆盖标记为private。请注意,仅当此类在文件中单独关闭时,这才有效。

另一种可能性是将所有初始值设定项(包括init(coder:))标记为convenience。现在你继承了init(),整个问题就消失了。但是,这引入了另一组问题,并留给读者练习。

澄清一下,我认为这整个情况是 Swift 中的一个错误。IIRC直到最近的修订版(Xcode 6.1?)才表现得像这样。你被super.init(customView:)打电话给你init()的事实所包围。你可以争辩说这是错误的;您有一个很好的错误报告用例。运行时的崩溃似乎是错误的行为;如果这会发生,为什么编译器不阻止我们?也许其他人可以证明 Swift 在这种情况下的所作所为是合理的。我只是想解释一下。

编辑 这个答案来自 2014 年,旨在解决当时存在的错误。在现代iOS版本中,该错误已消失,您可以这样说:

class LabelBarButtonItem: UIBarButtonItem {
    init(text: String) {
        let label = UILabel(frame: .zero)
        label.text = text
        label.sizeToFit()
        super.init()
        self.customView = label
    }
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

或者你可以这样说:

class LabelBarButtonItem: UIBarButtonItem {
    convenience init(text: String) {
        let label = UILabel(frame: .zero)
        label.text = text
        label.sizeToFit()
        self.init(customView: label)
    }
}

在最初提出这个问题的那一刻,这两种情况都是不可能的。

我不得不为一个项目对UIBarButtonItem进行子类化,遵循Matt的答案就绰绰有余了,只有一点点不同。我必须为初始值设定项增加便利性才能使代码正常工作。就我而言,我的子类中没有属性。

这是适用于 Swift 3 的代码,以防它对任何人有帮助。

class PlayerBarButtonItem: UIBarButtonItem {
convenience init(assetName: String, target: Any, selector: Selector) {
    let barButton = UIButton(frame: CGRect(x: 0, y: 0, width: 34, height: 34))
    let barButtonImage = UIImage(named: assetName)?.withRenderingMode(.alwaysTemplate)
    barButton.setImage(barButtonImage, for: .normal)
    barButton.tintColor = UIColor.white
    barButton.addTarget(target, action: selector, for: .touchUpInside)
    self.init(customView: barButton)
}
private override init() {
    super.init()
}
required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

最新更新