UIBarButtonItem导致此初始化程序继承问题的原因是什么?

时间:2014-11-24 22:47:44

标签: objective-c swift initializer designated-initializer

我试图创建一个简单的UIBarButtonItem的Swift子类:

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类子类化

时,出现了类似的问题

2 个答案:

答案 0 :(得分:6)

问题在于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中的一个错误。在最近的修订版(Xcode 6.1?)之前,它并没有像IIRC这样的行为。 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)
    }
}

在最初提出问题的时刻,这些都不可能。

答案 1 :(得分:4)

我必须将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")
}