2016-08-10 7 views
1

У меня есть два класса, которые идеально бы иметь код в их inits и deinits, например:Быстрая передовая практика для умножения наследования и деидов?

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. Мое текущее решение является сделать один протокол и положить записку, чтобы напомнить мне, чтобы написать init и deinit с вызовами в 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) } 
} 

Есть ли лучший способ?

ответ

0

Вы могли бы каждый протокол определить необходимый инициализатору:

protocol Tappable { 
    init(r:Registry) 
} 

Тогда любой класс, который наследует протокол должен будет реализовать этот инициализатор, который вы надеетесь бы напомнить вам, что должно было случиться там.

Это не работает особенно хорошо для подклассов UIView, которым также необходимо реализовать инициализаторы UIView.

+0

Благодарим за предложение, это, безусловно, хорошее напоминание. Я буду использовать эту идею, когда смогу, но я не всегда хочу иметь init с точной подписью. Хорошим побочным эффектом этой идеи является то, что она заставляет инъекцию зависимостей, которые иногда я также хочу. Еще раз спасибо. –

0

Вот еще одно решение, которое заменяет ваши два суперкласса одним суперклассом и OptionSet. Очевидно, что это становится немного громоздким, если вам нужно сделать много специфичных для случая инициализации и деинициализации, но он работает okay для приведенного примера.

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] 
     } 
    } 
} 
+0

Спасибо, я могу, наверное, сделать что-то подобное. –

2

Вы можете создать композитный класс и хранить классы реестра в качестве переменных, это может выглядеть следующим образом:

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) } 
    } 
} 

Таким образом, когда Deinit вызывается на UIElement объекта, все из черты UIElement будут сняты с учета.


Вы можете проверить это на Swift Playground, добавив следующее внизу. Это создаст класс UIElement, зарегистрирует ли он его свойства, а затем освободит его и отменит регистрацию!

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

Я попробую поблагодарить. –

0

У меня есть ущемленные идеи на детской площадке ниже. Благодарю.

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 += "tap\n" } 
} 
protocol Resizable { 
    func resize() 
} 
extension Resizable { 
    func resize() { sequence += "resize\n" } 
} 
class UIElement: Registrar, Tappable, Resizable { 
} 
var uie: UIElement! = UIElement() 
uie.tap() 
uie.resize() 
uie = nil 
sequence // "reg. tap.\nreg. res.\ntap\nresize\ndereg. tap.\ndereg. res.\n"