GCD in Swfit 3.0
这里包括了Queue, Group, Barrier, Semaphore等内容。基本上常用的GCD对象和方法在Swift3.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.
dispatch_group_wait
=>DispatchGroup#wati
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
GCD in Swfit 3.0的更多相关文章
- 浅入了解GCD 并发 并行 同步 异步 多线程
什么是 GCD?! GCD就是一个函数库(废话) 用来压榨系统的资源,解决多线程处理中一些问题的库(知道这个就够了,很多电影角色都是因为知道太多死得很惨!!!!!) 1.并发与并行 Concurre ...
- 创建线程方式-GCD
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- Swift 2.0初探
转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心. 今年6月,一年一度的WWDC大会如期而至,在大会上Apple发布了Swift 2.0,引 ...
- (转载)关于gcd的8题
发现其实有关gcd的题目还是挺多的,这里根据做题顺序写出8题. [bzoj2818: Gcd] gcd(x,y)=质数, 1<=x,y<=n的对数 做这题的时候,懂得了一个非常重要的转化: ...
- iOS开发——新特性OC篇&Swift 2.0新特性
Swift 2.0新特性 转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心.我同样也是非常喜爱这门新的编程语言. 今年6月,一年一度 ...
- Zoj 3868 GCD Expectation
给一个集合,大小为n , 求所有子集的gcd 的期望和 . 期望的定义为 这个子集的最大公约数的K次方 : 每个元素被选中的概率是等可能的 即概率 p = (发生的事件数)/(总的事件数); 总的事件 ...
- Swift 2.0初探:值得注意的新特性
转眼间,Swift已经一岁多了,这门新鲜.语法时尚.类型安全.执行速度更快的语言已经渐渐的深入广大开发者的心.我同样也是非常喜爱这门新的编程语言. 今年6月,一年一度的WWDC大会如期而至,在大会上A ...
- hdu1695 GCD
http://acm.hdu.edu.cn/showproblem.php?pid=16951 /** 大意: a<=x<=b , c<= y <= d ,求在此范围内 有多少 ...
- Revenge of GCD HDU5019
Description In mathematics, the greatest common divisor (gcd), also known as the greatest common fac ...
随机推荐
- Mysql Communication link failure :1153 Got a packet bigger than 'max_allowed_packet' bytes
出现这种情况: 临时解决方法是: 登录mysql: 执行: set global max_allowed_packet=1000000000; set global net_buffer_ ...
- HashMap的两种实现方式
本文主要简要分析了Java中和Redis中HashMap的实现,并且对比了两者的异同 1.Java的实现 下图表示了Java中一个HashMap的主要实现方式 因为大家对于Java中HashMap的实 ...
- 书单.md
0823 John Hoskin, An Ilustrated History of Thailand.Asia Books Co., Ltd.2015 0729 Gerald Graff, Cath ...
- Python“Non-ASCII character 'xe5' in file”报错问题(转)
今天在编译一个Python程序的时候,一直出现"Non-ASCII character 'xe5' in file"报错问题 SyntaxError: Non-ASCII char ...
- bootstrap 时间控件带(时分秒)选择器
1.控件下载地址:http://www.bootcss.com/p/bootstrap-datetimepicker/index.htm,参数设置说明也在这个链接下面: 2.具体参数说明(复制原链接) ...
- orm获取关联表里的属性值
ORM——关系对象模型 laravel中的Eloquent ORM用于和数据表互动,其中每个数据库表会和一个对应的「模型」互动,想要了解请查看官方文档或自行百度.获取关联表里的属性值代码如下: /** ...
- 使用yield关键字让自定义集合实现foreach遍历
一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代 ...
- 对CPU做下性能测试
任务管理器里把pi.exe优先级调到“高” I5-4308U (2.80Ghz) PI-1M最佳成绩11.719秒(MACBOOK PRO 13inch,插电,电源选择高性能模式) G202 ...
- redis cluster java client jedisCluster spring集成方法
1.使用jedis的原生JedisCluster spring的applicationContext.xml配置redis的连接.连接池.jedisCluster Bean <bean id=& ...
- .NET技术大系概览 (迄今为止最全的.NET技术栈)
从2002年的.NET 1.0开始,1.1,2.x,3.x,4.x,每个新版本的.NET都会增加新的技术,生态圈也在不断壮大. AD: 前言 .Net推出13年了,Visual Studio 2015 ...