Переопределение свойств 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")
}
}