如何用其超类的实例初始化我的子类?

Posted

技术标签:

【中文标题】如何用其超类的实例初始化我的子类?【英文标题】: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)

【问题讨论】:

【参考方案1】:

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

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

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

    使用地图将 NativeRectangles 链接到其他信息。在 Objective C 和 Swift 中,您可以通过 objc_AssociationPolicy 最轻松地拥有这样的内部映射。见https://***.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 解决方案。

以上是关于如何用其超类的实例初始化我的子类?的主要内容,如果未能解决你的问题,请参考以下文章

如何使子类只能从实例访问?

如果我使用Superclass初始化子类对象,为什么该对象具有子类的属性,但是超类的方法?

休眠:覆盖(实体)超类的 OneToMany 映射字段?

如何在 C++ 子类的构造函数中初始化超类的 const 成员变量?

从超类对象初始化子类实例

在其超类也具有相同名称的 ivar 的子类中拥有 ivar“委托”