【问题标题】:NSOperation property overrides (isExecuting / isFinished)NSOperation 属性覆盖(isExecuting / isFinished)
【发布时间】:2014-06-08 18:57:13
【问题描述】:

我在 Swift 中继承 NSOperation 并且需要覆盖 isExecutingisFinished 属性,因为我覆盖了 start 方法。

我遇到的问题是如何在保留键值观察 (KVO) 的同时还能够覆盖这些属性。

通常在 Obj-C 中,在类扩展 JSONOperation () 定义中将属性重新声明为 readwrite 是相当容易的。但是,我在 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)
        })
    }
}

所以这是我想出的解决方案,但感觉非常丑陋和hacky:

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,但是我有两个名称相似的属性,这会引入歧义并使其可公开写入(我不想要)。

哦,我会为一些访问修饰符关键字做什么...

【问题讨论】:

  • 顺便说一句,start 函数也应该检查if (cancelled) {...},如果是,则立即finish 操作并退出。

标签: ios macos swift nsoperation


【解决方案1】:

正如 David 所说,您可以在子类属性覆盖中同时实现 getter 和 setter。

但是,在定义asynchronous/concurrent 操作(即那些将异步完成的操作)时,为isFinishedisExecuting 调用will/didChangeValueForKey 至关重要。如果你不这样做,操作将不会被释放,依赖项将不会被兑现,你会遇到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 方法等时有时会很有用。


更新:

人们不止一次指出NSOperation.h 中的新finished/executing 属性,并得出结论认为适当的KVO 密钥将是finished/executing。一般来说,在编写符合 KVO 的属性时,这是正确的。

但是NSOperationQueue 没有观察到finished/executing 键。 它观察到了isFinished/isExecuting 键。如果您不对 isFinished/isExecuting 键执行 KVO 调用,您可能会遇到问题(尤其是异步操作之间的依赖关系将失败)。这很烦人,但这就是它的工作原理。 并发编程指南Operation Queues章节的为并发执行配置操作部分非常清楚需要执行isFinished/isExecuting的主题KVO 调用。

虽然并发编程指南已经过时,但它对isFinished/isExecuting KVO 的描述非常明确。并且可以很容易地凭经验验证该指南仍然反映了实际的NSOperation 实现。通过演示,在NSOperationQueue 中使用异步/并发NSOperation 子类时,请参阅this Github demonstration of the appropriate KVO 中的单元测试。

【讨论】:

  • 同意。但这不是NSOperation/NSOperationQueue 的实现方式。实际观察isFinished/isExecuting。这是非标准的;它很烦人;但这就是它的工作原理。我已经更新了我的答案,澄清并链接到适当键的演示(如果您不发布isFinished/isExecuting,则说明问题)。
  • 有趣的是,您可以使用operation.valueForKey("executing"),它按预期工作。
  • 也可以解释为什么几年前我无法让 operationCount 工作并最终创建自己的实现...
  • 仅供参考:我使用 Xcode 7.1 iOS 9.1 Swift 2.1 转换并运行了 "Github demonstration of the appropriate KVO"isFinished/isExecuting KVO 键通过。 finished/executing 键失败。
  • 我不知道它是什么时候发生的,但您现在可以使用“isFinished”或“finished”作为通知的关键路径,操作将正常工作。
【解决方案2】:

来自 swift 书:

您可以通过在子类属性覆盖中同时提供 getter 和 setter 来将继承的只读属性呈现为读写属性。

我想你会发现这是可行的:

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

【讨论】:

  • 谢谢!这个解决方案现在将尽可能好。 _executing 的名称暗示它是私有的并且应该防止大多数程序员错误,即使它确实泄漏了封装。如果没有变量的访问修饰符,我们对此无能为力。
  • 是的,实现和接口的主要缺点之一是相同的文件,尤其是没有访问修饰符。
  • @David 更接近,但 NSOperation 正在观察 isFinishedisExecuting 通知,而不是 finishedexecuting。因此,在您的示例中,您必须对 isExecuting 键名而不是 executing 进行 KVN 调用。
  • 属性名称为“finished”和“executing” 访问器名称为“isFinished”和“isExecuting”: property (readonly, getter=isExecuting) BOOL正在执行;属性(只读,getter=isFinished) BOOL 完成;
  • 同意。但可悲的是,NSOperationQueue 正在观察isFinished/isExecuting,尽管这不是标准的 KVO 键,人们期望使用名为finished/executing 的属性。
【解决方案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")
    }
}

【讨论】:

  • 太荒谬了,Apple 开箱即用地提供了这个。我们需要提出自己的自定义解决方案......为什么没有任何 ConcurrentOperation 类可以继承?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多