GCD in Swfit 3.0

主要是嘚瑟一下英文。其实代码都在,什么文都无所谓。代码在这里:https://github.com/future-challenger/Swift3.0/tree/master/GCD

This project is “forked” from raywenderlich GCD tutorial. It’s really a good tutorial where I learned what I wanted. But it’s kinda out of date. In Swift 3.0, lots of API in iOS SDK have been modified. Including how GCD APIs are called. So I update the tutorial to swift 3.0

Create a block

before:

      let block = dispatch_block_create(DISPATCH_BLOCK_INHERIT_QOS_CLASS) { // 3
        // things to do in this block
      }

swift 3.0

      let block = DispatchWorkItem{
        let index = Int(i)
        let address = addresses[index]
        let url = URL(string: address)
        let photo = DownloadPhoto(url: url!) {
          image, error in
          if let error = error {
            storedError = error
          }
          downloadGroup.leave()
        }
        PhotoManager.sharedManager.addPhoto(photo)
      }

Create a Queue

Concurrent Queue

before:

let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue", DISPATCH_QUEUE_CONCURRENT)

swift 3.0

let concurrentQueue = DispatchQueue(label: "com.swift3.imageQueue", attributes: .concurrent)
concurrentQueue.async {
  print("async task")
}  
Serial Queue

before:

let concurrentQueue = dispatch_queue_create("com.swift3.imageQueue", DISPATCH_QUEUE_SERIAL)

swift 3.0

let concurrentQueue = DispatchQueue(label: "com.swift3.imageQueue")
concurrentQueue.sync {
  print("sync task")
}  

Main Queue

dispatch_get_main_queue => DispatchQueue.main

Global Queue

dispatch_get_global_queue => DispatchQueue.global(qos:)
before:

dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.value), 0)

Swift 3.0

DispatchQueue.global(qos: .userInteractive)

Here’s a easy one. Before we always do things like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    // do something background
    dispatch_async(dispatch_get_main_queue(), ^{
        // update UI in main thread(or UI thread)
    });
});

In swift 3.0, we do it this way.

DispatchQueue.global(qos: .userInitiated).async {
    // background things
    DispatchQueue.main.async {
        print("main thread dispatch")
    }
}

Dispatch After & Once

Dispatch After

before you do dispatch after like this:

var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
    // your function here
})

In swift 3.0

let dispatchTime: DispatchTime = DispatchTime.now() + Double(Int64(0.1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {
    // your function here
})

or even more simply:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
    // your function here
}
Disaptch Once

This dispatch_once on longer exists in Swift 3.0.

According to Apple’s migration guide:

The free function dispatch_once is no longer available in Swift. In Swift, you can use lazily initialized globals or static properties and get the same thread-safety and called-once guarantees as dispatch_once provided.

You can use lazy initialized global or static properties instead of dispatch once. eg:

// global constant: SomeClass initializer gets called lazily, only on first use
let foo = SomeClass()

// global var, same thing happens here
// even though the "initializer" is an immediately invoked closure
var bar: SomeClass = {
    let b = SomeClass()
    b.someProperty = "whatever"
    b.doSomeStuff()
    return b
}()

// ditto for static properties in classes/structures/enums
class MyClass {
    static let singleton = MyClass()
    init() {
        print("foo")
    }
}
Dispatch Once Is Still Needed

Global var or static property can not meet our needs when we just need some code run once in app. And this code has a reference to self. Static property makes this not possible. Let’s checkout some other ways to use “dispatch onde” in Swift 3.0.
It fits Singleton very well, but not the run-once thing.

The first one:

public extension DispatchQueue {
    private static var _onceTracker = [String]()

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block:@noescape(Void)->Void) {
        objc_sync_enter(self); defer { objc_sync_exit(self) }

        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)
        block()
    }
}

How to use the once function:

DispatchQueue.once(token: "com.vectorform.test") {
    print( "Do This Once!" )
}

or:

private let _onceToken = NSUUID().uuidString

DispatchQueue.once(token: _onceToken) {
    print( "Do This Once!" )
}

NOTE: You have to use your own tracker to prevent your code run more than once.

Let’s make some improvement:

public extension DispatchQueue {
    private static var _onceTracker = [String]()

    public class func once(file: String = #file, function: String = #function, line: Int = #line, block:(Void)->Void) {
        let token = file + ":" + function + ":" + String(line)
        once(token: token, block: block)
    }

    /**
     Executes a block of code, associated with a unique token, only once.  The code is thread safe and will
     only execute the code once even in the presence of multithreaded calls.

     - parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
     - parameter block: Block to execute once
     */
    public class func once(token: String, block:(Void)->Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }


        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)
        block()
    }
}

How to use it:

DispatchQueue.once {
    setupUI()
}

or:

DispatchQueue.once(token: "com.me.project") {
    setupUI()
}

You can use a string tracker, you also can use the default tracker.

But there’s another way. You can define another name for dispatch_once in an ObjC file, and use it in swift 3.0 with the “Bridege Header” imported.

// in header
typedef dispatch_once_t mxcl_dispatch_once_t;
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block);

// in source file
void mxcl_dispatch_once(mxcl_dispatch_once_t *predicate, dispatch_block_t block) {
    dispatch_once(predicate, block);
}

You can use mxcl_dispatch_once in swift.

Create Dispatch Source

before:

    let queue = dispatch_get_main_queue()
    self.signalSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL,
                                               UInt(SIGSTOP), 0, queue) // 3
    if let source = self.signalSource { // 4
      dispatch_source_set_event_handler(source) { // 5
        NSLog("Hi, I am: \(self.description)")
      }
      dispatch_resume(source) // 6
    }

Swift 3.0:

    let queue = DispatchQueue.main
    self.signalSource = DispatchSource.makeSignalSource(signal: 0, queue: queue) // 3
    if let source = self.signalSource { // 4
        source.setEventHandler(handler: { // 5
        print("Hi, I am: \(self.description)")
        })
        source.resume() // 6
    }

Dispatch Barrier

When you add things in a multithreaded enviroment, you have to prevent more than one thread try to add things in the same time. You can use Barrier to do this.

before:

  dispatch_barrier_async(currentQueue) { // NOTE: barrier, requires exclusive access for write
    //...
  }

Swift 3.0

    concurrentPhotoQueue.async(flags: .barrier, execute: { // 1
        self._photos.append(photo) // 2
        GlobalMainQueue.async { // 3
        self.postContentAddedNotification()
        }
    }) 

Dispatch Group

How to create one:

var downloadGroup = dispatch_group_create()

Swift 3.0

let downloadGroup = DispatchGroup()

Sometimes we want to start a new queue when tasks running in other background queues all finished. Dispatch group help us with that. There’re two ways to achieve this.
1. dispatch_group_wait => DispatchGroup#wati
2. dispatch_group_notify => DispatchGroup#notify

Let’s see how they work.

You want dispatch group wait work, there’re other tow methods you have to know: dispatch_group_enter, dispatch_group_leave. The enter method manually notify the group that a task has started. The leave method has to be called the same time as the enter method has called. Or you app may crash.

Dispatch Group Wait
// some unrelevant code is removed.
  @IBAction func groupWaitAction(_ sender: AnyObject) {
    let concurrentQueue = DispatchQueue(label: "com.gcd.demo.concurrent", attributes: .concurrent)
    concurrentQueue.async {
      let taskGroup = DispatchGroup()
      for i in 0..<100 {
        taskGroup.enter()

        print("###task \(i) \n")
        Thread.sleep(forTimeInterval: 0.5)

        taskGroup.leave()
      }

      taskGroup.wait()

      DispatchQueue.main.async {
        print("It's on main queue now")
      }
    }
  }

First of all, dispatch group in this example is run in a concurrent queue. I did not notice this in the beginning. And you should notice that the wait method would block all thread. If any of the tasks takes a lot of time, things will be bad. Fortunally, dispatch group can wait with a timeout parameter. If the time expires before all tasks are done, it will return a non-zero value. With dispatch group wait, you have to dispatch to another queue (mostly the main queue) manually.

Dispatch Group notify
  @IBAction func groupWaitAction(_ sender: AnyObject) {
    let concurrentQueue = DispatchQueue(label: "com.gcd.demo.concurrent", attributes: .concurrent)
    concurrentQueue.async {
      let taskGroup = DispatchGroup()
      for i in 0..<100 {
        taskGroup.enter()

        print("###task \(i) \n")
        Thread.sleep(forTimeInterval: 0.5)

        taskGroup.leave()
      }

      taskGroup.notify(queue: DispatchQueue.main, work: DispatchWorkItem(block: {
        print("It's on main queue now")
      }))
    }
  }

The best way to use DispatchGroup is to send a group in a concurrent queue then wait or notifiy. @hen all things are done, dispatch to Main queue to update UI.

Dispatch Apply

Before Swift 3.0, there’s a very good method to handle iterations. It’s dispatch_apply. This method ia a sync method, not return until all tasks in its loop are done. But tasks in the method to iterate are executed concurrently. Now in swift 3.0, it got a new name: DispatchQueue.concurrentPerform.

It’s always a good option to use DispatchQueue.concurrentPerform in a concurrent queue but not a good one in a serial queue.

But how to use DispatchQueue.concurrentPerform to improve the Dispatch Group Wait code? Let’s give it a shot.

  @IBAction func dispatchApplyAction(_ sender: AnyObject) {
    let concurrentQueue = DispatchQueue(label: "com.apply.gcd", attributes: .concurrent)
    let taskGroup = DispatchGroup()

    concurrentQueue.async {
      DispatchQueue.concurrentPerform(iterations: 50, execute: {index in
        taskGroup.enter()
        print(">>>task \(index) \n")
        Thread.sleep(forTimeInterval: 0.5)
        taskGroup.leave()
      })

      taskGroup.notify(queue: DispatchQueue.main, work: DispatchWorkItem(block: {
        print(">>>It's on main queue now")
      }))
    }
  }

Run DispatchQueue.concurrentPerform code in a background thread, this will not block the main thread while tasks are running. When all work is done, DispatchGroup wil use notify to update the UI thread.

Semaphore

  @IBAction func semaphoreAction(_ sender: AnyObject) {
    let semaphore = DispatchSemaphore(value: 0)

    Thread.sleep(forTimeInterval: 1);
    semaphore.signal()


    let returnVal = semaphore.wait(timeout: DispatchTime(uptimeNanoseconds: 800000000))
    if (returnVal == .timedOut) {
      print("%%%Semaphore timeout")
    }
  }

Here’s how to create one, how to single it and wait until semaphore is available.

reference:

http://stackoverflow.com/questions/37801407/whither-dispatch-once-in-swift-3
http://stackoverflow.com/questions/37801436/how-do-i-write-dispatch-after-gcd-in-swift-3
http://stackoverflow.com/questions/37886994/dispatch-once-in-swift-3

### Swift 中创建和管理 Timer 的方法 在 Swift 中,`Timer` 是一个非常实用的工具,可以用来实现计时功能。通过 `Timer` 类,开发者可以轻松地创建定时器,并在特定的时间间隔触发回调函数。以下是一个详细的介绍,包括如何创建、开始、停止和中断计时器。 #### 创建 Timer 在 Swift 中,可以通过 `Timer.scheduledTimer` 方法来创建并启动一个定时器。此方法需要指定时间间隔(以秒为单位)、目标对象、选择器方法、用户信息以及是否重复执行。例如: ```swift import Foundation var timer: Timer? func startTimer() { timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in print("每秒触发一次") } } ``` 以上代码创建了一个每隔一秒触发一次的定时器[^1]。 #### 停止 Timer 要停止一个正在运行的 `Timer`,可以调用其 `invalidate()` 方法。这将销毁定时器对象并停止其触发回调函数。例如: ```swift func stopTimer() { timer?.invalidate() timer = nil } ``` #### 暂停和恢复 Timer 由于 `Timer` 本身没有直接提供暂停和恢复的功能,因此可以通过设置标志位或重新创建定时器来模拟这一行为。例如: ```swift var isPaused = false var lastFireDate: Date? func pauseTimer() { if let timer = timer { lastFireDate = timer.fireDate timer.fireDate = Date.distantFuture isPaused = true } } func resumeTimer() { if let timer = timer, isPaused { timer.fireDate = lastFireDate ?? Date() isPaused = false } } ``` 上述代码通过调整 `fireDate` 来实现暂停和恢复功能[^2]。 #### 在游戏场景中的应用 在游戏开发中,`Timer` 可以用于控制游戏的逻辑更新频率,例如角色移动、敌人生成或分数计算等。为了确保游戏逻辑的流畅性,可以结合主线程调度机制来执行这些任务。例如: ```swift DispatchQueue.main.async { self.updateGameLogic() } ``` 这种做法确保了所有 UI 更新操作都在主线程上完成[^3]。 #### 注意事项 - `Timer` 的精度可能受到系统负载的影响,因此不适合对时间要求极高的场景。 - 如果需要更精确的计时功能,可以考虑使用 `CADisplayLink` 或者自定义基于 `DispatchSourceTimer` 的解决方案[^4]。 ### 示例代码 以下是一个完整的示例,展示了如何在 Swift 中创建、开始、停止和暂停计时器: ```swift import Foundation class GameTimerManager { var timer: Timer? var isPaused = false var lastFireDate: Date? func startTimer() { timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in print("计时器触发") } } func stopTimer() { timer?.invalidate() timer = nil } func pauseTimer() { if let timer = timer { lastFireDate = timer.fireDate timer.fireDate = Date.distantFuture isPaused = true } } func resumeTimer() { if let timer = timer, isPaused { timer.fireDate = lastFireDate ?? Date() isPaused = false } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值