继承init和deint的快捷最佳实践



我有两个类,理想情况下会在它们的initsdeinits中包含代码,例如:

class Tappable {
    init() { Registry.register(tappable: self) }
    deinit { Registry.deregister(tappable: self) }
}
class Resizable {
    init() { Registry.register(resizable: self) }
    deinit { Registry.deregister(resizable: self) }
}

理想情况下,我将同时继承两者,例如:

class UIElement: Tappable, Resizable {}

但是在Swift中当然不能。我目前的解决方案是做一个协议,并放一个笔记,提醒我写initdeinit调用Registry,例如:

//: Classes that implememt `Resizable` must call `Registry.register(resizable: self)` in all `init`s and have `deinit { Registry.deregister(resizable: self) }`.
protocol Resizable {}
class UIElement: Tappable, Resizable {
    override init() {
        super.init()
        Registry.register(resizable: self)
    }
    deinit { Registry.deregister(resizable: self) }
}

有更好的方法吗?

您可以创建一个复合类并将注册表类存储为变量,它看起来像这样:

protocol Register {
    init(_ target: UIElement)
    func deregister(target: UIElement)
}
class Tappable: Register {
    required init(_ target: UIElement) { Registry.register(tappable: target) }
    func deregister(target: UIElement) { Registry.deregister(tappable: target) }
}
class Resizable: Register {
    required init(_ target: UIElement) { Registry.register(resizable: target) }
    func deregister(target: UIElement) { Registry.deregister(resizable: target) }
}
class UIElement {
    var traits: [Register]!
    override init() {
        self.traits = [Tappable(self), Resizable(self)]
    }
    deinit {
        self.traits.forEach { $0.deregister(self) }
    }
}

这样,当在UIElement对象上调用deinit时,UIElement的所有特征将被注销。


你可以在Swift Playground中进行测试,方法是在底部添加以下代码。这将创建UIElement类,让它注册特征,然后释放它并取消注册!

var test: UIElement! = UIElement()
test = nil

你可以让每个协议定义一个必需的初始化器:

protocol Tappable {
    init(r:Registry)
}

那么任何继承该协议的类都必须实现该初始化项,您希望这将提醒您需要在那里发生什么。

对于需要实现UIView的指定初始化器的UIView子类来说,这并不能很好地工作。

这是另一个解决方案,它用一个超类和一个OptionSet替换两个超类。显然,如果您需要执行大量特定于大小写的初始化和反初始化操作,那么这会变得有点笨拙,但对于给出的示例,它可以

class Registry {
    class func register(resizeable: Any) {
    }
    class func register(tappable: Any) {
    }
}
struct ViewTraits: OptionSet {
    let rawValue: Int
    init(rawValue: Int) { self.rawValue = rawValue }
    static let Tappable = ViewTraits(rawValue: 1)
    static let Resizeable = ViewTraits(rawValue: 2)
}
protocol Traits {
    var traits:ViewTraits { get }
}
class TraitedView: NSView, Traits {
    var traits:ViewTraits {
        get {
            fatalError("Must implement a getter for Traits")
        }
    }
    private func register() {
        if (traits.contains(.Tappable)) {
            Registry.register(tappable: self)
        }
        if (traits.contains(.Resizeable)) {
            Registry.register(resizeable: self)
        }
    }
    override init(frame:NSRect) {
        super.init(frame: frame)
        register()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        register()
    }
}
class MyView: TraitedView {
    override var traits: ViewTraits {
        get {
            return [ViewTraits.Resizeable, ViewTraits.Tappable]
        }
    }
}

我已经在下面的操场上捏住了每个人的想法。谢谢。

var sequence = ""
enum Registry {
    static func register(tappable _: Tappable) { sequence += "reg. tap.n" }
    static func deregister(tappable _: Tappable) { sequence += "dereg. tap.n" }
    static func register(resizable _: Resizable) { sequence += "reg. res.n" }
    static func deregister(resizable _: Resizable) { sequence += "dereg. res.n" }
}
class Registrar {
    init() {
        if let tappable = self as? Tappable {
            Registry.register(tappable: tappable)
        }
        if let resizable = self as? Resizable {
            Registry.register(resizable: resizable)
        }
    }
    deinit {
        if let tappable = self as? Tappable {
            Registry.deregister(tappable: tappable)
        }
        if let resizable = self as? Resizable {
            Registry.deregister(resizable: resizable)
        }
    }
}
protocol Tappable {
    func tap()
}
extension Tappable {
    func tap() { sequence += "tapn" }
}
protocol Resizable {
    func resize()
}
extension Resizable {
    func resize() { sequence += "resizen" }
}
class UIElement: Registrar, Tappable, Resizable {
}
var uie: UIElement! = UIElement()
uie.tap()
uie.resize()
uie = nil
sequence // "reg. tap.nreg. res.ntapnresizendereg. tap.ndereg. res.n"

最新更新