首先來看一段代碼,而後猜想輸出結果,注意輸出內容的順序:bash
class Dog: NSObject {
@objc
func getDogName() {
debugPrint("ハチ公")
}
}
var runloop: CFRunLoop!
let sem = DispatchSemaphore(value: 0)
let thread = Thread {
RunLoop.current.add(NSMachPort(), forMode: .common)
runloop = CFRunLoopGetCurrent()
sem.signal()
CFRunLoopRun()
}
thread.start()
sem.wait()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
CFRunLoopPerformBlock(runloop, CFRunLoopMode.defaultMode.rawValue) {
debugPrint("2")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
debugPrint("1")
let dog = Dog()
dog.perform(#selector(dog.getDogName), with: nil)
})
}
RunLoop.current.run()
複製代碼
輸出結果爲1
、ハチ公
。async
2
咱們其實已經將block
任務加到了thread
對應的runloop
中了,可是此時的runloop
已經處於休眠狀態,一直沒有被喚醒,因此block
內任務一直得不到執行。oop
block
內方法要想執行到block
內方法咱們須要喚醒runloop
,可使用ui
let dog = Dog()
dog.perform(#selector(dog.getDogName), with: nil)
CFRunLoopWakeUp(runloop)
複製代碼
這樣runloop
被喚醒,block
方法獲得了執行,輸出結果爲1
、ハチ公
、2
。spa
timer
的方式喚醒runloop
的方式有多種,有一張圖描述的很不錯:線程
timer
的方式,喚醒
runloop
:
let dog = Dog()
dog.perform(#selector(dog.getDogName), on: thread, with: nil, waitUntilDone: false)
複製代碼
這個方法會在指定線程添加一個定時器,而後執行相應方法。這樣經過添加定時器方法成功喚醒runloop
,又由於block
添加到任務隊列較早因此輸出順序是1
、2
、ハチ公
。debug
perform
的幾個方法public func perform(_ aSelector: Selector!) -> Unmanaged<AnyObject>!
複製代碼
這個方法是NSObjectProtocol
內的方法,會在當前線程經過消息發送機制調用方法。code
open func perform(_ aSelector: Selector, on thr: Thread, with arg: Any?, waitUntilDone wait: Bool)
複製代碼
這個方法是NSObject
在Thread
文件下的一個extension
,他能夠指定線程而且添加一個timer
去執行這個任務,這就是爲何能夠喚醒runloop
的緣由。waitUntilDone
這個參數能夠設置是否等到selector
方法執行完畢後,執行後面的代碼。orm
open func perform(_ aSelector: Selector, with anArgument: Any?, afterDelay delay: TimeInterval)
複製代碼
這個方法是NSObject
在Runloop
文件下的一個extension
。與上一個方法相似,只不過默認執行在當前線程。而且能夠設定delay
時間。cdn
runloop
若是咱們將代碼改成:
let thread = Thread {
// RunLoop.current.add(NSMachPort(), forMode: .common)
runloop = CFRunLoopGetCurrent()
sem.signal()
CFRunLoopRun()
}
let dog = Dog()
dog.perform(#selector(dog.getDogName), on: thread, with: nil, waitUntilDone: false)
複製代碼
這樣只會打印1
。由於雖然咱們獲取到了當前線程的runloop
,可是因爲runloop
沒有添加timer
或source
,因此會立刻釋放掉,這樣線程也被釋放掉了,因此再去操做這個線程就得不到任何結果了。