Nandin Borjigin
Nandin Borjigin

Reputation: 2154

How to port a complicated abstract class to swift?

I have an abstract class in my mind and I can't implement its several features in swift, so I use C++ to deliver my thoughts:

template <class T>
class Swapping {
public:
    void swap() { _foregroundIndex = backgroundIndex() }
    virtual void cleanup() = 0;
    T* foreground() { return _buffer[foregroundIndex()]; }
    T* background() { return _buffer[backgroundIndex()]; }
    void setForeground(T* foreground) { _buffer[foregroundIndex()] = foreground; }
    void setBackground(T* background) { _buffer[backgroundIndex()] = background; }
private: 
    short foregroundIndex() { return _foregroundIndex; } 
    short backgroundIndex() { return _foregroundIndex ^ 1; }
    short _foregroundIndex = 0;
    T* _buffer[2] = {NULL, NULL};
}

The main contradiction is that

  1. The pure virtual method cleanup() requires all subclasses to implement it explicitly (can achieve in swift with protocol)
  2. The instance variable _foregroundIndex has an initial value (cannot achieve using protocol)
  3. The instance variable _foregroundIndex is restricted to be private ( cannot achieve using protocol)

On the other hand, if I use a class instead of protocol, then I can't guarantee cleanup() method is overriden.

One may suggest that put the virtual method in a protocol and the instance variable in a class. That may work but is not a obsession-satisfying one.

P.S. Objective-C is not Swift. Any objc_runtime related workaround is not preferred.

Upvotes: 3

Views: 73

Answers (1)

idmean
idmean

Reputation: 14895

There’s an obvious solution, which I have seen often but will certainly not satisfy you is:

func cleanup() {
    fatalError("You must override cleanup()")
}

Then you could try using extensions to extend the protocol with default implementations, but extensions don’t allow stored properties and so you would most likely need some external objects or other magic you certainly also dislike.

As I noted above in the comments, you might need to rethink your design. I don’t know what you really intend to do, but maybe something like this would work out for you:

class Swapper<T> {
    private var foregroundIndex = 0
    private var backgroundIndex: Int {
        return foregroundIndex ^ 1
    }
    private var buffer: [T?] = [nil, nil]

    private let cleanupHandler: () -> ()

    init(cleanupHandler: @escaping () -> ()) {
        self.cleanupHandler = cleanupHandler
    }

    func cleanup() {
        cleanupHandler()
    }

    var foreground: T? {
        get {
            return buffer[foregroundIndex]
        }
        set {
            buffer[foregroundIndex] = newValue
        }
    }
    var background: T? {
        get {
            return buffer[backgroundIndex]
        }
        set {
            buffer[backgroundIndex] = newValue
        }
    }

    func swap() {
        foregroundIndex = backgroundIndex
    }
}

This makes more sense to me as this allows any types to be swapped with any clean up handler, without having to subclass the class every time.

Upvotes: 2

Related Questions