【问题标题】:How to init my subclass with an instance of its superclass?如何用其超类的实例初始化我的子类?
【发布时间】:2017-08-26 10:08:20
【问题描述】:

在我的应用程序中,我读取了 EKEvent 类型的日历事件,并使用大量计算变量进行了扩展,因此我可以轻松获取日历中每个事件的持续时间、工时数等。但在大规模情况下,性能很差 - 所以我想改用惰性变量来缓存我所有的额外数据。

因此,我想创建一个 EKEvent 的子类 - 称为 CustomEvent,它添加了惰性变量,但我的问题是 EKEventStore 总是返回 EKEvents,我需要将其转换为我的 CustomEvent 子类的实例,以便能够访问惰性变量等。

一个简单的类型转换是不够的,我在操场上尝试过,看看有什么可行的方法,但没有任何用处。我需要一个 CustomRectangle 的特殊构造函数,它可以从 NativeRectangle 初始化一个 CustomRectangle。另一种解决方案是创建一个将原始对象作为属性保存的包装类,但这不是我最喜欢的解决方案,因为我必须映射所有方法和属性

class NativeRectangle: NSObject {
    var width: Int
    var height: Int

    init(width: Int, height: Int) {
        self.width = width
        self.height = height
        super.init()
    }
}

class CustomRectangle: NativeRectangle {
    var area: Int { return width * height}
}

let rect = NativeRectangle(width: 100, height: 20)

let customRect = CustomRectangle(rect) // This fails, i need a constructor

print(customRect.area)

【问题讨论】:

    标签: ios swift casting subclassing eventkit


    【解决方案1】:

    在 Swift 中(通常在大多数面向对象语言中)没有办法在创建子类实例时使用基类对象的现有实例。

    从一般编程的角度来看,在这种情况下您有两种选择:

    1. 使用组合:使 CustomRectangle 包含一个 NativeRectangle 并将所有需要的方法转发给它。

    2. 使用地图将 NativeRectangles 链接到其他信息。在 Objective C 和 Swift 中,您可以通过 objc_AssociationPolicy 最轻松地拥有这样的内部映射。见https://stackoverflow.com/a/43056053/278842

    顺便说一句。您无法从“缓存”width * height 的简单计算中看到任何加速。

    【讨论】:

    • 当我不得不做这样的事情时,通常首先想到的是构图。
    • 谢谢 - 当我必须创建一个包装类并映射属性时,我想......我希望有一个更优雅的解决方案。 width*height 的简单“缓存”仅用于示例...
    【解决方案2】:

    如果您已经在 Objective-C 领域工作,则可以选择包装原生类并自动转发所有(添加的除外)消息:

    - (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
    {
        NSMethodSignature *ours = [super methodSignatureForSelector:selector];
        return ours ?: [wrappedObject methodSignatureForSelector:selector];
    }
    

    我不记得这是否是转发工作所需的一切,但应该非常接近。另外,我不知道这将如何与 Swift 一起使用,所以我想我们可以将其视为 Objective-C 时代的有趣琐事,并寻找更好的解决方案……


    我想到的第二个,同样有点骇人听闻的选项是使用associated objects feature 将缓存数据链接到原始实例。这样你就可以保持你的扩展方法。

    【讨论】:

      【解决方案3】:

      您创建了自己的 CustomRectangle(object: rect) ,因此 swift 将不再提供默认的 init() 。您明确需要致电您自己持有您的财产的人并致电super.init(),因为您的类也继承自超类。 -

      class NativeRectangle: NSObject {
          var width: Int
          var height: Int
      
          // Super class custom init()
          init(width: Int, height: Int) {
              self.width = width
              self.height = height
              super.init()
          }
      }
      
      class CustomRectangle: NativeRectangle {
      
          // computed property area
          var area: Int { return width * height}
      
          // Sub class Custom Init
          init(object:NativeRectangle) {
              // call to super to check proper initialization
              super.init(width: object.width, height: object.height)
          }
      }
      
      let rect = NativeRectangle(width: 100, height: 20)
      
      let customRect = CustomRectangle(object: rect)
      
      print(customRect.area) //2000
      

      【讨论】:

      • 我认为这不是 EKEvents 的解决方案,因为它们没有可用的适当构造函数。但对于其他类型,它可能很有用。我会坚持@Christopher Oezbek 的建议
      • @Esben von Buchwald 不太了解 EKEvents 或初始化如何为它工作。但上面的答案是在 swift 或很可能在任何其他语言中遵循的基本初始化过程。事实上,你可以坚持使用 Oezbek 解决方案。
      猜你喜欢
      • 2013-07-27
      • 2016-06-13
      • 1970-01-01
      • 2019-10-12
      • 1970-01-01
      • 1970-01-01
      • 2013-04-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多