Skip to content

Commit 7922083

Browse files
authored
Merge pull request #947 from ianpartridge/async-operation-3.1
2 parents e88389c + a35cb4d commit 7922083

File tree

2 files changed

+93
-2
lines changed

2 files changed

+93
-2
lines changed

Foundation/NSOperation.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ open class Operation : NSObject {
4747
#endif
4848
}
4949

50-
/// - 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.
5150
open func start() {
5251
main()
5352
finish()
@@ -159,6 +158,24 @@ open class Operation : NSObject {
159158
}
160159
}
161160

161+
/// The following two methods are added to provide support for Operations which
162+
/// are asynchronous from the execution of the operation queue itself. On Darwin,
163+
/// this is supported via KVO notifications. In the absence of KVO on non-Darwin
164+
/// platforms, these two methods (which are defined in NSObject on Darwin) are
165+
/// temporarily added here. They should be removed once a permanent solution is
166+
/// found.
167+
extension Operation {
168+
public func willChangeValue(forKey key: String) {
169+
// do nothing
170+
}
171+
172+
public func didChangeValue(forKey key: String) {
173+
if key == "isFinished" && isFinished {
174+
finish()
175+
}
176+
}
177+
}
178+
162179
extension Operation {
163180
public enum QueuePriority : Int {
164181
case veryLow

TestFoundation/TestNSOperationQueue.swift

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import XCTest
1616
import SwiftFoundation
1717
import SwiftXCTest
1818
#endif
19+
import Dispatch
1920

2021
class TestNSOperationQueue : XCTestCase {
2122
static var allTests: [(String, (TestNSOperationQueue) -> () throws -> Void)] {
2223
return [
2324
("test_OperationPriorities", test_OperationPriorities),
24-
("test_OperationCount", test_OperationCount)
25+
("test_OperationCount", test_OperationCount),
26+
("test_AsyncOperation", test_AsyncOperation)
2527
]
2628
}
2729

@@ -65,4 +67,76 @@ class TestNSOperationQueue : XCTestCase {
6567
XCTAssertEqual(msgOperations[2], "Operation2 executed")
6668
XCTAssertEqual(msgOperations[3], "Operation4 executed")
6769
}
70+
71+
func test_AsyncOperation() {
72+
let operation = AsyncOperation()
73+
XCTAssertFalse(operation.isExecuting)
74+
XCTAssertFalse(operation.isFinished)
75+
76+
operation.start()
77+
78+
while !operation.isFinished {
79+
// do nothing
80+
}
81+
82+
XCTAssertFalse(operation.isExecuting)
83+
XCTAssertTrue(operation.isFinished)
84+
}
85+
}
86+
87+
class AsyncOperation: Operation {
88+
89+
private let queue = DispatchQueue(label: "async.operation.queue")
90+
private let lock = NSLock()
91+
92+
private var _executing = false
93+
private var _finished = false
94+
95+
override internal(set) var isExecuting: Bool {
96+
get {
97+
return _executing
98+
}
99+
set {
100+
if _executing != newValue {
101+
willChangeValue(forKey: "isExecuting")
102+
_executing = newValue
103+
didChangeValue(forKey: "isExecuting")
104+
}
105+
}
106+
}
107+
108+
override internal(set) var isFinished: Bool {
109+
get {
110+
return _finished
111+
}
112+
set {
113+
if _finished != newValue {
114+
willChangeValue(forKey: "isFinished")
115+
_finished = newValue
116+
didChangeValue(forKey: "isFinished")
117+
}
118+
}
119+
}
120+
121+
override var isAsynchronous: Bool {
122+
return true
123+
}
124+
125+
override func start() {
126+
if isCancelled {
127+
isFinished = true
128+
return
129+
}
130+
131+
isExecuting = true
132+
133+
queue.async {
134+
sleep(1)
135+
self.lock.lock()
136+
self.isExecuting = false
137+
self.isFinished = true
138+
self.lock.unlock()
139+
}
140+
}
141+
68142
}

0 commit comments

Comments
 (0)