检查 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 停止了整个过程,而不是只让测试失败。

回答我自己的问题:

基于 ,我现在使用 setSpecificgetSpecific

这将创建一个密钥,将其设置在测试队列中,稍后再次获取它:

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