Merge pull request #924 from ianpartridge/nsregularexpression
diff --git a/Foundation/NSOperation.swift b/Foundation/NSOperation.swift
index fcc9b38..988aef8 100644
--- a/Foundation/NSOperation.swift
+++ b/Foundation/NSOperation.swift
@@ -47,7 +47,6 @@
#endif
}
- /// - Note: Operations that are asynchronous from the execution of the operation queue itself are not supported since there is no KVO to trigger the finish.
open func start() {
main()
finish()
@@ -159,6 +158,24 @@
}
}
+/// The following two methods are added to provide support for Operations which
+/// are asynchronous from the execution of the operation queue itself. On Darwin,
+/// this is supported via KVO notifications. In the absence of KVO on non-Darwin
+/// platforms, these two methods (which are defined in NSObject on Darwin) are
+/// temporarily added here. They should be removed once a permanent solution is
+/// found.
+extension Operation {
+ public func willChangeValue(forKey key: String) {
+ // do nothing
+ }
+
+ public func didChangeValue(forKey key: String) {
+ if key == "isFinished" && isFinished {
+ finish()
+ }
+ }
+}
+
extension Operation {
public enum QueuePriority : Int {
case veryLow
diff --git a/TestFoundation/TestNSOperationQueue.swift b/TestFoundation/TestNSOperationQueue.swift
index cc74b86..783115f 100644
--- a/TestFoundation/TestNSOperationQueue.swift
+++ b/TestFoundation/TestNSOperationQueue.swift
@@ -16,12 +16,14 @@
import SwiftFoundation
import SwiftXCTest
#endif
+import Dispatch
class TestNSOperationQueue : XCTestCase {
static var allTests: [(String, (TestNSOperationQueue) -> () throws -> Void)] {
return [
("test_OperationPriorities", test_OperationPriorities),
- ("test_OperationCount", test_OperationCount)
+ ("test_OperationCount", test_OperationCount),
+ ("test_AsyncOperation", test_AsyncOperation)
]
}
@@ -65,4 +67,76 @@
XCTAssertEqual(msgOperations[2], "Operation2 executed")
XCTAssertEqual(msgOperations[3], "Operation4 executed")
}
+
+ func test_AsyncOperation() {
+ let operation = AsyncOperation()
+ XCTAssertFalse(operation.isExecuting)
+ XCTAssertFalse(operation.isFinished)
+
+ operation.start()
+
+ while !operation.isFinished {
+ // do nothing
+ }
+
+ XCTAssertFalse(operation.isExecuting)
+ XCTAssertTrue(operation.isFinished)
+ }
+}
+
+class AsyncOperation: Operation {
+
+ private let queue = DispatchQueue(label: "async.operation.queue")
+ private let lock = NSLock()
+
+ private var _executing = false
+ private var _finished = false
+
+ override internal(set) var isExecuting: Bool {
+ get {
+ return _executing
+ }
+ set {
+ if _executing != newValue {
+ willChangeValue(forKey: "isExecuting")
+ _executing = newValue
+ didChangeValue(forKey: "isExecuting")
+ }
+ }
+ }
+
+ override internal(set) var isFinished: Bool {
+ get {
+ return _finished
+ }
+ set {
+ if _finished != newValue {
+ willChangeValue(forKey: "isFinished")
+ _finished = newValue
+ didChangeValue(forKey: "isFinished")
+ }
+ }
+ }
+
+ override var isAsynchronous: Bool {
+ return true
+ }
+
+ override func start() {
+ if isCancelled {
+ isFinished = true
+ return
+ }
+
+ isExecuting = true
+
+ queue.async {
+ sleep(1)
+ self.lock.lock()
+ self.isExecuting = false
+ self.isFinished = true
+ self.lock.unlock()
+ }
+ }
+
}