Swift 子类化 - 如何覆盖 Init()

Posted

技术标签:

【中文标题】Swift 子类化 - 如何覆盖 Init()【英文标题】:Swift subclassing - how to override Init() 【发布时间】:2014-08-22 16:46:15 【问题描述】:

我有以下类,带有一个 init 方法:

class user 
  var name:String
  var address:String

  init(nm: String, ad: String) 
    name = nm
    address = ad
  

我正在尝试对此类进行子类化,但在 super.init() 部分不断出现错误:

class registeredUser : user 
     var numberPriorVisits: Int

     // This is where things start to go wrong - as soon as I type 'init' it 
     // wants to autocomplete it for me with all of the superclass' arguments, 
     // and I'm not sure if those should go in there or not:
     init(nm: String, ad: String)   
        // And here I get errors:
        super.init(nm: String, ad: String) 

     // etc....

Apple 的 iBook 有子类化的示例,但没有那些具有 init() 方法且其中包含任何实际参数的功能类。他们所有的 init 都没有参数。

那么,你是怎么做到的呢?

【问题讨论】:

我认为你可以(并且应该)通过以大写的方式声明类来使你的代码更简洁? 【参考方案1】:

除了 Chuck 的回答之外,您还必须在调用 super.init 之前初始化新引入的属性

指定的初始化器必须确保所有属性 由它的类引入的在它委托给一个之前被初始化 超类初始化器。 (Swift 编程语言 -> 语言指南 -> 初始化)

因此,让它发挥作用:

init(nm: String, ad: String) 
    numberPriorVisits = 0  
    super.init(nm: nm, ad: ad) 

这个简单的初始化为零也可以通过将属性的默认值设置为零来完成。也鼓励这样做:

var numberPriorVisits: Int = 0

如果您不想要这样的默认值,那么扩展您的初始化程序以同时为新属性设置新值是有意义的:

init(name: String, ads: String, numberPriorVisits: Int) 
    self.numberPriorVisits = numberPriorVisits
    super.init(nm: name, ad: ads)

【讨论】:

好吧,我确实希望 不必 必须将默认值 0 设置为 numberPriorVisits - 但你是说它实际上是鼓励的?你能分享你在哪里了解到它的鼓励吗?此外,总的来说,我试图了解 Swift 中的 init's 的最佳实践是什么。给你的所有属性一个默认值并让你的 init 方法不带参数会更好吗?还是最好有任何默认值并通过将参数传递给init来完成这一切? @sirab:鼓励我的意思是,如果你想将它初始化为某个值,那么鼓励使用属性的默认值的方式,而不是在初始化器。好吧,如果您的解决方案使用默认值,则不需要初始化参数。 @sirab:我了解到阅读语言指南会鼓励这样做。与我在回答中提到的相同章节。它说“如果一个属性总是采用相同的初始值,请提供一个默认值而不是在初始化程序中设置一个值。最终结果是相同的,但是......”去阅读:-) 好的,这是有道理的——但仅适用于确实总是采用相同的默认初始值的属性。但我认为用户名或地址不太适合使用方便的默认值 - 除非您想使用空格,例如:“”。但这似乎有点粗糙。就像“好吧,让我们把它留空,这样我们至少有 something,因此可以避免编写 init 方法 - 知道我的意思吗?问题是:你是否认为默认使用空格字符串属性的值是最佳实践吗?(我真的想在这里找出最佳方法。) @sirab:这完全取决于您的需求。因此,没有最佳实践。恕我直言,对于您的示例,根据参数在初始化程序中设置名称和地址并将访问次数设置为默认值 0 或 1,因为用户是新用户,这看起来很明显。【参考方案2】:

在 swift 2.0 及更高版本中它的工作方式是这样的(所有情况)

init(newString:String) 
    super.init(string:newString)
    // Designed initialiser 

override init(someString: String) 
    super.init(mainString: someString)
    // Override initialiser when subclass some class 

required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")
    // Some boilerplate code to handle error (needed when override)

convenience init(newString:String, withParameters:Dictionary<String,String>) 
    self.init(someString:newString)
    //Convenience initialiser 

【讨论】:

【参考方案3】:

您将参数传递给初始化程序,就像您将参数传递给普通方法一样:

init(nm: String, ad: String)   
    super.init(nm: nm, ad: ad) 

作为参考,这显示在 Swift 语言指南的 Designated and Convenience Initializers In Action 部分。

【讨论】:

【参考方案4】:

您是否尝试过设置 numberPriorVisits 的值并将调用类型更改为 super

class user 
    var name:String
    var address:String

    init(nm: String, ad: String) 
        name = nm
        address = ad
    



class registeredUser : user 
    var numberPriorVisits: Int;

    init(nm: String, ad: String) 
        self.numberPriorVisits = 0;
        super.init(nm: nm, ad: ad)
    

【讨论】:

以上是关于Swift 子类化 - 如何覆盖 Init()的主要内容,如果未能解决你的问题,请参考以下文章

Swift init 方法错误:声明 'init(coder:)' 不能覆盖多个超类声明

使用 Swift3 子类化 CALayer + 隐式动画

如何覆盖子类的 swift 协议函数(例如 UIView 中的 UILabel)

Swift 错误 - 方法不覆盖其超类中的任何方法

子类化 UIApplication 以覆盖 sendEvent 导致崩溃

快速覆盖 UIView 中的默认图层类型