NSOperation property overrides (isExecuting / isFinished)

北城以北 提交于 2019-12-18 10:23:16

问题


I am subclassing NSOperation in Swift and need to override the isExecuting and isFinished properties since I am overriding the start method.

The problem I run into is how to preserve key-value observing (KVO) while also being able to override these properties.

Normally in Obj-C this would be rather easy to redeclare the properties as readwrite in the class extension JSONOperation () definition. However, I don't see this same capability in Swift.

Example:

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

So here is the solution I have come up with, but it feels awfully ugly and 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 }
}

Please tell me there is a better way. I know I could make a var isExecuting instead of the whole struct, but then I have two similarly named properties which introduces ambiguity and also makes it publicly writable (which I do not want).

Oh what I would do for some access modifier keywords...


回答1:


From the swift book:

You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override.

I think you'll find that this works:

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



回答2:


As David said, you can implement both a getter and setter in the subclass property override.

But, when defining asynchronous/concurrent operations (i.e. those operations that will complete asynchronously), it is critical to call the will/didChangeValueForKey for isFinished and isExecuting. If you don't, operations won't be released, dependencies won't be honored, you'll have problems is maxConcurrentOperationCount, etc.).

So I would therefore suggest:

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

By the way, checking to see if _executing and _finished have changed is not critical, but it can sometimes be useful when writing custom cancel methods or the like.


Update:

More than once, people have pointed to the new finished/executing properties in NSOperation.h and concluded that the appropriate KVO keys would be finished/executing. Generally, when writing KVO-compliant properties, that would be correct.

But NSOperationQueue does not observe the finished/executing keys. It observes the isFinished/isExecuting keys. If you don't perform the KVO calls for the isFinished/isExecuting keys, you can have problems (notably dependencies between asynchronous operations will fail). It's annoying, but that's how it works. The Configuring Operations for Concurrent Execution section of the Operation Queues chapter of the Concurrency Programming Guide is very clear on the topic of needing to perform the isFinished/isExecuting KVO calls.

While the Concurrency Programming Guide is dated, it's quite explicit regarding the isFinished/isExecuting KVO. And one can easily empirically validate that the guide still reflects the actual NSOperation implementation. By way of demonstration, see the unit tests in this Github demonstration of the appropriate KVO when using asynchronous/concurrent NSOperation subclass in NSOperationQueue.




回答3:


Swift 3.0 Answer Update:

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


来源:https://stackoverflow.com/questions/24109701/nsoperation-property-overrides-isexecuting-isfinished

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!