【问题标题】:What causes this initializer inheritance issue with UIBarButtonItem?是什么导致 UIBarButtonItem 的初始化程序继承问题?
【发布时间】:2023-03-10 08:22:01
【问题描述】:

我正在尝试创建一个简单的 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 类时,也出现了类似的问题

【问题讨论】:

标签: objective-c swift initializer designated-initializer


【解决方案1】:

问题是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)
    }
}

在最初提出问题的那一刻,这两种方法都不可能。

【讨论】:

  • 好的,有道理。但是现在如果我重写init(),我的类的客户可以通过调用LabelBarButtonItem()(没有传入默认文本)来创建一个无效的LabelBarButtonItem 实例。我可以覆盖init(),删除到超类,并以某种方式使这个初始化程序隐藏到外部代码中吗?
  • @Bill 我已经大大扩展了我的答案,将您的问题纳入其中并将我的 cmets 移入其中。看看你的想法......
  • 感谢您的解释 - 我很感激!我刚刚向 Apple 提交了错误报告。
  • @Bill 我也提交了一份,以您的代码的缩短版本为例;这是一个很好的用例。
  • @JAL 啊。这是 Cocoa 中的一个错误,它在下一个主要版本中得到修复。与 Swift 无关(除了 Swift 会引发错误)。
【解决方案2】:

我不得不为一个项目继承 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")
}

【讨论】:

  • 正确,initWithCustomView 是一个方便的初始化程序,因此应该像您所做的那样与 self 链接。
  • 永远不需要覆盖initinit(coder:)。只需使您的初始化程序成为便利初始化程序并停止即可。这是因为 Apple 修复了 init(customView:) 是指定初始化程序的错误。现在它是一个便利的初始化器,因为它应该一直都是。 2017 年没有需要解决的错误。
猜你喜欢
  • 1970-01-01
  • 2018-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
相关资源
最近更新 更多