Check if on correct dispatch queue in Swift 3

点点圈 提交于 2019-12-09 04:36:15

问题


I have a few unit tests in which I'd like to test if a callback is called on the correct dispatch queue.

In Swift 2, I compared the label of the current queue to my test queue. However in Swift 3 the DISPATCH_CURRENT_QUEUE_LABEL constant no longer exists.

I did find the dispatch_assert_queue function. Which seems to be what I need, but I'm not sure how to call it.

My Swift 2 code:

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

Update:

I got confused by the lack of autocomplete, but it is possible to use __dispatch_assert_queue:

if #available(iOS 10.0, *) {
  __dispatch_assert_queue(test1Queue)
}

While this does work for unit tests, it annoyingly stops the whole process with a EXC_BAD_INSTRUCTION instead of only failing a test.


回答1:


Use dispatchPrecondition(.onQueue(expectedQueue)), the Swift 3 API replacement for the dispatch_assert_queue() C API.

This was covered in the WWDC 2016 GCD session (21:00, Slide 128): https://developer.apple.com/videos/play/wwdc2016/720/




回答2:


Answering my own question:

Based on KFDoom's comments, I'm now using setSpecific and getSpecific.

This creates a key, sets it on the test queue, and later on, gets it again:

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

Note that there's no value associated with the key (its type is Void), I'm only interested in the existence of the specific, not in it's value.

Important!
Make sure to keep a reference to the key, or cleanup after you're done using it. Otherwise a newly created key could use the same memory address, leading to weird behaviour. See: http://tom.lokhorst.eu/2018/02/leaky-abstractions-in-swift-with-dispatchqueue




回答3:


Tests based on KFDoom's answer:

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

}



回答4:


One option is to set a precondition to test directly for the queue or set "specific" on it and retrieve it later. Further, one could use setSpecific and getSpecific. Alternatively, you can use a precondition check if you're on a queue so that should fulfill the "get current" need. src: https://github.com/duemunk/Async/blob/feature/Swift_3.0/AsyncTest/AsyncTests.swift

and

https://github.com/apple/swift/blob/master/stdlib/public/SDK/Dispatch/Dispatch.swift




回答5:


One related option is to set a Main Queue / UI Queue precondition:

dispatchPrecondition(condition: .onQueue(DispatchQueue.main))



回答6:


/*
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")
}


来源:https://stackoverflow.com/questions/37952262/check-if-on-correct-dispatch-queue-in-swift-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!