Переопределение свойств NSOperation (isExecuting/isFinished)

Я подклассифицирую NSOperation в Swift и должен переопределить свойства isExecuting и isFinished, так как я переопределяю метод start.

Проблема, с которой я сталкиваюсь, заключается в том, как сохранить наблюдение за ключом (KVO), а также переопределить эти свойства.

Обычно в Obj-C было бы довольно легко переопределить свойства как readwrite в определении расширения класса JSONOperation (). Тем не менее, я не вижу такой же возможности в Swift.

Пример:

class JSONOperation : NSOperation, NSURLConnectionDelegate
{
    var executing : Bool
    {
        get { return super.executing }
        set { super.executing } // ERROR: readonly in the superclass
    }

    // Starts the asynchronous NSURLConnection on the main thread
    override func start()
    {
        self.willChangeValueForKey("isExecuting")
        self.executing = true
        self.didChangeValueForKey("isExecuting")

        NSOperationQueue.mainQueue().addOperationWithBlock(
        {
            self.connection = NSURLConnection(request: self.request, delegate: self, startImmediately: true)
        })
    }
}

Итак, вот решение, которое я придумал, но он чувствует себя ужасно уродливым и взломанным:

var state = Operation()

struct Operation
{
    var executing = false
    var finished = false
}

override var executing : Bool
{
    get { return state.executing }
    set { state.executing = newValue }
}

override var finished : Bool
{
    get { return state.finished }
    set { state.finished = newValue }
}

Скажи, что есть лучший способ. Я знаю, что я мог бы сделать var isExecuting вместо целого struct, но тогда у меня есть два одинаково названных свойства, которые вводят двусмысленность, а также делают его общедоступным для записи (чего я не хочу).

О, что бы я сделал для некоторых ключевых слов-модификаторов доступа...

Ответы

Ответ 1

Из быстрой книги:

Вы можете представить унаследованное свойство только для чтения как свойство read-write, предоставив как getter, так и setter в свойстве переопределения подкласса.

Думаю, вы обнаружите, что это работает:

override var executing : Bool {
    get { return _executing }
    set { 
        willChangeValueForKey("isExecuting")
        _executing = newValue 
        didChangeValueForKey("isExecuting")
    }
}
private var _executing : Bool

Ответ 2

Как сказал Дэвид, вы можете реализовать как getter, так и setter в переопределении свойства подкласса.

Но при определении операций asynchronous/concurrent (т.е. тех операций, которые будут выполняться асинхронно), необходимо вызвать will/didChangeValueForKey для isFinished и isExecuting. Если вы этого не сделаете, операции не будут выпущены, зависимости не будут выполнены, у вас будут проблемы maxConcurrentOperationCount и т.д.).

Поэтому я бы поэтому предложил:

private var _executing: Bool = false
override var executing: Bool {
    get {
        return _executing
    }
    set {
        if _executing != newValue {
            willChangeValueForKey("isExecuting")
            _executing = newValue
            didChangeValueForKey("isExecuting")
        }
    }
}

private var _finished: Bool = false;
override var finished: Bool {
    get {
        return _finished
    }
    set {
        if _finished != newValue {
            willChangeValueForKey("isFinished")
            _finished = newValue
            didChangeValueForKey("isFinished")
        }
    }
}

Кстати, проверка того, изменились ли _executing и _finished, не является критическим, но иногда может быть полезна при написании пользовательских методов cancel или тому подобного.


Update:

Несколько раз люди указывали на новые свойства finished/executing в NSOperation.h и пришли к выводу, что соответствующие ключи KVO будут finished/executing. Как правило, при написании KVO-совместимых свойств это было бы правильно.

Но NSOperationQueue не соблюдает клавиши finished/executing. Он замечает клавиши isFinished/isExecuting. Если вы не выполняете вызовы KVO для клавиш isFinished/isExecuting, у вас могут быть проблемы (в частности, зависимости между асинхронными операциями не будут выполняться). Это раздражает, но то, как это работает. Раздел "Конфигурирование операций для параллельного выполнения" главы Очереди операций в руководстве по программированию Concurrency очень прост по теме: isFinished/isExecuting KVO.

В то время как руководство по программированию Concurrency датировано, оно довольно явно относится к isFinished/isExecuting KVO. И можно легко эмпирически подтвердить, что руководство по-прежнему отражает фактическую реализацию NSOperation. В качестве демонстрации см. Блок-тесты в этой демонстрации Github соответствующего KVO при использовании асинхронного/параллельного подкласса NSOperation в NSOperationQueue.

Ответ 3

Обновление ответов Swift 3.0:

private var _executing : Bool = false
override var isExecuting : Bool {
    get { return _executing }
    set {
        guard _executing != newValue else { return }
        willChangeValue(forKey: "isExecuting")
        _executing = newValue
        didChangeValue(forKey: "isExecuting")
    }
}


private var _finished : Bool = false
override var isFinished : Bool {
    get { return _finished }
    set {
        guard _finished != newValue else { return }
        willChangeValue(forKey: "isFinished")
        _finished = newValue
        didChangeValue(forKey: "isFinished")
    }
}