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

Bil*_*ill 11 objective-c initializer designated-initializer swift

我正在尝试创建一个简单的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")
  }
}
Run Code Online (Sandbox Code Playgroud)

但是当我尝试实例化这个:

let countButton = LabelBarButtonItem(text: "0 items")
Run Code Online (Sandbox Code Playgroud)

代码编译正确但在运行时失败:

fatal error: use of unimplemented initializer 'init()' for class 'TestProject.LabelBarButtonItem'
Run Code Online (Sandbox Code Playgroud)

我无法理解为什么在这种情况下会发生这种情况,或者一般来说是什么原则导致这个问题.在init(text)初始化应直接委托给init(customView)超类.为什么要init()打电话?它不应该涉及.

任何人都可以解释发生了什么吗?当我试图继承其他UIKit类时,会出现类似的问题

mat*_*att 11

问题是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")
    }
}
Run Code Online (Sandbox Code Playgroud)

在你的评论中,你问:

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

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

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

为了澄清,我认为整个情况都是Swift中的一个错误.在最近的一次修订(Xcode 6.1?)之前,它没有像IIRC这样的行为.您正在被这样的事实所约束super.init(customView:)调用你的 init().你可以说这是错的; 你有一个错误报告的好用例.运行时崩溃似乎也是错误的行为; 如果发生这种情况,为什么编译器不会阻止我们呢?也许其他人可以证明Swift在这种情况下做了什么.我只想解释一下.

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

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

或者你可以这样说:

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

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


Nah*_*dan 5

我不得不为一个项目继承 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")
}
Run Code Online (Sandbox Code Playgroud)