2017-06-30 30 views
1

私は非同期操作をサポートするためにOperationをサブクラス化しました。 新しいクラスはAsyncOperationと呼ばれ、state という新しいフィールドが追加されました。これはenumであり、操作状態の管理に役立ちます。オペレーション状態スレッドセーフではない

class AsyncOperation: Operation { 
// DONE: State enum with keyPath property 
enum State: String { 
    case Ready, Executing, Finished 

fileprivate var keyPath: String { 
    return "is" + rawValue 
    } 
} 

// DONE: state property 
var state = State.Ready { 
    willSet { 
    willChangeValue(forKey: newValue.keyPath) 
    willChangeValue(forKey: state.keyPath) 
    } 
    didSet { 
    didChangeValue(forKey: oldValue.keyPath) 
    didChangeValue(forKey: state.keyPath) 
    } 
} 
} 




extension AsyncOperation { 
// DONE: Operation Overrides 
override var isReady: Bool { 
return super.isReady && state == .Ready 
} 

override var isExecuting: Bool { 
    return state == .Executing 
} 

override var isFinished: Bool { 
    return state == .Finished 
} 

override var isAsynchronous: Bool { 
    return true 
} 

override func start() { 
    if isCancelled { 
    state = .Finished 
    return 
} 
main() 
state = .Executing 
} 

override func cancel() { 
    state = .Finished 
} 

} 

一般に、このサブクラスは大きく動作し、非常に満足しています。タフないくつかの奇妙な行動を経験 イム... いくつかのケースでは、IMはそうのようなキューに操作を追加:私はブレークポイントをチェックした場合の動作は何とかすでに、キューに派遣しているため

//this code happens in mainViewController 

//op is an operation that belong to mainViewController and could dispatched to the queue from many places, its init called once in view did load. 
op = SomeAsyncOperation() 


if(op.state == .Executing){ 
    queue.addOperatiom(op) 
} 

とアプリがクラッシュします私が作成したstateプロパティはReadyであり、生の操作のisExecutingフィールドはtrueです。私のstateプロパティが発生し、操作状態フィールドが同期されません。別の実装でstateフィールドを確認すると、ExecutingFinishedになります。どのようにすればそれらを常に同期させることができますか?

答えて

1

stateプロパティへの読み書きを保護するには、NSLockを使用する必要があります。

重要な部分であるWWDC 2015のセッションAdvanced NSOperationのサンプルコードを見て

してください:

/// Private storage for the `state` property that will be KVO observed. 
private var _state = State.Initialized 

/// A lock to guard reads and writes to the `_state` property 
private let stateLock = NSLock() 

private var state: State { 
    get { 
     return stateLock.withCriticalScope { 
      _state 
     } 
    } 

    set(newState) { 
     /* 
      It's important to note that the KVO notifications are NOT called from inside 
      the lock. If they were, the app would deadlock, because in the middle of 
      calling the `didChangeValueForKey()` method, the observers try to access 
      properties like "isReady" or "isFinished". Since those methods also 
      acquire the lock, then we'd be stuck waiting on our own lock. It's the 
      classic definition of deadlock. 
     */ 
     willChangeValueForKey("state") 

     stateLock.withCriticalScope { Void -> Void in 
      guard _state != .Finished else { 
       return 
      } 

      assert(_state.canTransitionToState(newState), "Performing invalid state transition.") 
      _state = newState 
     } 

     didChangeValueForKey("state") 
    } 
} 
関連する問題