检查 Swift 中是否在正确的调度队列中 3
Check if on correct dispatch queue in Swift 3
我有几个单元测试,我想在其中测试是否在正确的调度队列上调用回调。
在Swift2中,我将当前队列的标签与我的测试队列进行了比较。但是在 Swift 3 中 DISPATCH_CURRENT_QUEUE_LABEL
常数不再存在。
我确实找到了 dispatch_assert_queue
函数。这似乎是我需要的,但我不确定如何调用它。
我的Swift2代码:
let testQueueLabel = "com.example.my-test-queue"
let testQueue = dispatch_queue_create(testQueueLabel, nil)
let currentQueueLabel = String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))!
XCTAssertEqual(currentQueueLabel, testQueueLabel, "callback should be called on specified queue")
更新:
我对缺少自动完成感到困惑,但可以使用 __dispatch_assert_queue
:
if #available(iOS 10.0, *) {
__dispatch_assert_queue(test1Queue)
}
虽然这确实适用于单元测试,但它令人恼火地用 EXC_BAD_INSTRUCTION
停止了整个过程,而不是只让测试失败。
回答我自己的问题:
基于 ,我现在使用 setSpecific
和 getSpecific
。
这将创建一个密钥,将其设置在测试队列中,稍后再次获取它:
let testQueueLabel = "com.example.my-test-queue"
let testQueue = DispatchQueue(label: testQueueLabel, attributes: [])
let testQueueKey = DispatchSpecificKey<Void>()
testQueue.setSpecific(key: testQueueKey, value: ())
// ... later on, to test:
XCTAssertNotNil(DispatchQueue.getSpecific(key: testQueueKey), "callback should be called on specified queue")
请注意,键没有关联的值(它的类型是 Void
),我只对具体的存在感兴趣,而不是它的值。
重要!
确保保留对密钥的引用,或在使用完后进行清理。否则,新创建的密钥可能会使用相同的内存地址,从而导致奇怪的行为。参见:http://tom.lokhorst.eu/2018/02/leaky-abstractions-in-swift-with-dispatchqueue
一个选项是设置先决条件以直接测试队列或对其设置 "specific" 并稍后检索。此外,可以使用 setSpecific 和 getSpecific。或者,如果您在队列中,则可以使用先决条件检查,这样就可以满足 "get current" 的需要。来源:https://github.com/duemunk/Async/blob/feature/Swift_3.0/AsyncTest/AsyncTests.swift
和
https://github.com/apple/swift/blob/master/stdlib/public/SDK/Dispatch/Dispatch.swift
测试基于 :
import XCTest
import Dispatch
class TestQueue: XCTestCase {
func testWithSpecificKey() {
let queue = DispatchQueue(label: "label")
let key = DispatchSpecificKey<Void>()
queue.setSpecific(key:key, value:())
let expectation1 = expectation(withDescription: "main")
let expectation2 = expectation(withDescription: "queue")
DispatchQueue.main.async {
if (DispatchQueue.getSpecific(key: key) == nil) {
expectation1.fulfill()
}
}
queue.async {
if (DispatchQueue.getSpecific(key: key) != nil) {
expectation2.fulfill()
}
}
waitForExpectations(withTimeout: 1, handler: nil)
}
func testWithPrecondition() {
let queue = DispatchQueue(label: "label")
let expectation1 = expectation(withDescription: "main")
let expectation2 = expectation(withDescription: "queue")
DispatchQueue.main.async {
dispatchPrecondition(condition: .notOnQueue(queue))
expectation1.fulfill()
}
queue.async {
dispatchPrecondition(condition: .onQueue(queue))
expectation2.fulfill()
}
waitForExpectations(withTimeout: 1, handler: nil)
}
}
使用dispatchPrecondition(.onQueue(expectedQueue))
,将Swift3API替换为dispatch_assert_queue()
CAPI.
WWDC 2016 GCD session(21:00,幻灯片 128)对此进行了介绍:
https://developer.apple.com/videos/play/wwdc2016/720/
/*
Dispatch queue and NSOperations in Swift 3 Xcode 8
*/
protocol Container {
associatedtype ItemType
var count: Int { get }
mutating func pop()
mutating func push(item: ItemType)
mutating func append(item: ItemType)
subscript(i: Int) -> ItemType { get }
}
//Generic Function
struct GenericStack<Element> : Container {
mutating internal func push(item: Element) {
items.append(item)
}
mutating internal func pop() {
items.removeLast()
}
var items = [ItemType]()
internal subscript(i: Int) -> Element {
return items[i]
}
mutating internal func append(item: Element) {
self.push(item: item)
}
internal var count: Int { return items.count }
typealias ItemType = Element
}
var myGenericStack = GenericStack<String>()
myGenericStack.append(item: "Narendra")
myGenericStack.append(item: "Bade")
myGenericStack.count
myGenericStack.pop()
myGenericStack.count
//Some NSOperation
class ExploreOperationAndThread {
func performOperation() {
//Create queue
let queue = OperationQueue()
let operation1 = BlockOperation {
var count = myGenericStack.count
while count > 0 {
myGenericStack.pop()
count -= 1
}
}
operation1.completionBlock = {
print("Operation 1")
}
let operation2 = BlockOperation {
var count = 0
while count == 10 {
myGenericStack.append(item: "ItemAdded")
count += 1
}
}
operation2.completionBlock = {
print("Operation 2")
print(myGenericStack.items)
}
//Suppose operation 3 is related to UI
let operation3 = BlockOperation {
//run on main thread
DispatchQueue.main.async {
print(myGenericStack.items.count)
}
}
operation3.completionBlock = {
print("Operation 3")
print(myGenericStack.items.count)
}
//add operation into queue
queue.addOperation(operation3)
queue.addOperation(operation1)
queue.addOperation(operation2)
//Limit number of concurrent operation in queue
queue.maxConcurrentOperationCount = 1
//add dependancies
operation1.addDependency(operation2)
operation2.addDependency(operation3)
if myGenericStack.items.count == 0 {
//remove dependency
operation1.removeDependency(operation2)
}
}
}
//Other ways of using queues
DispatchQueue.global(qos: .userInitiated).async {
ExploreOperationAndThread().performOperation()
}
DispatchQueue.main.async {
print("I am performing operation on main theread asynchronously")
}
OperationQueue.main.addOperation {
var count = 0
while count == 10 {
myGenericStack.append(item: "Narendra")
count += 1
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5 , execute: {
ExploreOperationAndThread().performOperation()
})
let queue2 = DispatchQueue(label: "queue2") //Default is serial queue
queue2.async {
print("asynchronously")
}
一个相关选项是设置主队列/UI队列前提条件:
dispatchPrecondition(condition: .onQueue(DispatchQueue.main))
我有几个单元测试,我想在其中测试是否在正确的调度队列上调用回调。
在Swift2中,我将当前队列的标签与我的测试队列进行了比较。但是在 Swift 3 中 DISPATCH_CURRENT_QUEUE_LABEL
常数不再存在。
我确实找到了 dispatch_assert_queue
函数。这似乎是我需要的,但我不确定如何调用它。
我的Swift2代码:
let testQueueLabel = "com.example.my-test-queue"
let testQueue = dispatch_queue_create(testQueueLabel, nil)
let currentQueueLabel = String(UTF8String: dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL))!
XCTAssertEqual(currentQueueLabel, testQueueLabel, "callback should be called on specified queue")
更新:
我对缺少自动完成感到困惑,但可以使用 __dispatch_assert_queue
:
if #available(iOS 10.0, *) {
__dispatch_assert_queue(test1Queue)
}
虽然这确实适用于单元测试,但它令人恼火地用 EXC_BAD_INSTRUCTION
停止了整个过程,而不是只让测试失败。
回答我自己的问题:
基于 setSpecific
和 getSpecific
。
这将创建一个密钥,将其设置在测试队列中,稍后再次获取它:
let testQueueLabel = "com.example.my-test-queue"
let testQueue = DispatchQueue(label: testQueueLabel, attributes: [])
let testQueueKey = DispatchSpecificKey<Void>()
testQueue.setSpecific(key: testQueueKey, value: ())
// ... later on, to test:
XCTAssertNotNil(DispatchQueue.getSpecific(key: testQueueKey), "callback should be called on specified queue")
请注意,键没有关联的值(它的类型是 Void
),我只对具体的存在感兴趣,而不是它的值。
重要!
确保保留对密钥的引用,或在使用完后进行清理。否则,新创建的密钥可能会使用相同的内存地址,从而导致奇怪的行为。参见:http://tom.lokhorst.eu/2018/02/leaky-abstractions-in-swift-with-dispatchqueue
一个选项是设置先决条件以直接测试队列或对其设置 "specific" 并稍后检索。此外,可以使用 setSpecific 和 getSpecific。或者,如果您在队列中,则可以使用先决条件检查,这样就可以满足 "get current" 的需要。来源:https://github.com/duemunk/Async/blob/feature/Swift_3.0/AsyncTest/AsyncTests.swift
和
https://github.com/apple/swift/blob/master/stdlib/public/SDK/Dispatch/Dispatch.swift
测试基于
import XCTest
import Dispatch
class TestQueue: XCTestCase {
func testWithSpecificKey() {
let queue = DispatchQueue(label: "label")
let key = DispatchSpecificKey<Void>()
queue.setSpecific(key:key, value:())
let expectation1 = expectation(withDescription: "main")
let expectation2 = expectation(withDescription: "queue")
DispatchQueue.main.async {
if (DispatchQueue.getSpecific(key: key) == nil) {
expectation1.fulfill()
}
}
queue.async {
if (DispatchQueue.getSpecific(key: key) != nil) {
expectation2.fulfill()
}
}
waitForExpectations(withTimeout: 1, handler: nil)
}
func testWithPrecondition() {
let queue = DispatchQueue(label: "label")
let expectation1 = expectation(withDescription: "main")
let expectation2 = expectation(withDescription: "queue")
DispatchQueue.main.async {
dispatchPrecondition(condition: .notOnQueue(queue))
expectation1.fulfill()
}
queue.async {
dispatchPrecondition(condition: .onQueue(queue))
expectation2.fulfill()
}
waitForExpectations(withTimeout: 1, handler: nil)
}
}
使用dispatchPrecondition(.onQueue(expectedQueue))
,将Swift3API替换为dispatch_assert_queue()
CAPI.
WWDC 2016 GCD session(21:00,幻灯片 128)对此进行了介绍: https://developer.apple.com/videos/play/wwdc2016/720/
/*
Dispatch queue and NSOperations in Swift 3 Xcode 8
*/
protocol Container {
associatedtype ItemType
var count: Int { get }
mutating func pop()
mutating func push(item: ItemType)
mutating func append(item: ItemType)
subscript(i: Int) -> ItemType { get }
}
//Generic Function
struct GenericStack<Element> : Container {
mutating internal func push(item: Element) {
items.append(item)
}
mutating internal func pop() {
items.removeLast()
}
var items = [ItemType]()
internal subscript(i: Int) -> Element {
return items[i]
}
mutating internal func append(item: Element) {
self.push(item: item)
}
internal var count: Int { return items.count }
typealias ItemType = Element
}
var myGenericStack = GenericStack<String>()
myGenericStack.append(item: "Narendra")
myGenericStack.append(item: "Bade")
myGenericStack.count
myGenericStack.pop()
myGenericStack.count
//Some NSOperation
class ExploreOperationAndThread {
func performOperation() {
//Create queue
let queue = OperationQueue()
let operation1 = BlockOperation {
var count = myGenericStack.count
while count > 0 {
myGenericStack.pop()
count -= 1
}
}
operation1.completionBlock = {
print("Operation 1")
}
let operation2 = BlockOperation {
var count = 0
while count == 10 {
myGenericStack.append(item: "ItemAdded")
count += 1
}
}
operation2.completionBlock = {
print("Operation 2")
print(myGenericStack.items)
}
//Suppose operation 3 is related to UI
let operation3 = BlockOperation {
//run on main thread
DispatchQueue.main.async {
print(myGenericStack.items.count)
}
}
operation3.completionBlock = {
print("Operation 3")
print(myGenericStack.items.count)
}
//add operation into queue
queue.addOperation(operation3)
queue.addOperation(operation1)
queue.addOperation(operation2)
//Limit number of concurrent operation in queue
queue.maxConcurrentOperationCount = 1
//add dependancies
operation1.addDependency(operation2)
operation2.addDependency(operation3)
if myGenericStack.items.count == 0 {
//remove dependency
operation1.removeDependency(operation2)
}
}
}
//Other ways of using queues
DispatchQueue.global(qos: .userInitiated).async {
ExploreOperationAndThread().performOperation()
}
DispatchQueue.main.async {
print("I am performing operation on main theread asynchronously")
}
OperationQueue.main.addOperation {
var count = 0
while count == 10 {
myGenericStack.append(item: "Narendra")
count += 1
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5 , execute: {
ExploreOperationAndThread().performOperation()
})
let queue2 = DispatchQueue(label: "queue2") //Default is serial queue
queue2.async {
print("asynchronously")
}
一个相关选项是设置主队列/UI队列前提条件:
dispatchPrecondition(condition: .onQueue(DispatchQueue.main))