概述
早上起床,你先打开洗衣机,然后用热水把泡面泡上,接着打开电脑开启一天的码农生活。其中“洗衣服”、“泡泡面”和“码代码”3个任务(线程)同时进行,这就是多线程。网上有许多关于多线程的经典解释,此处就不再菜鸟弄斧了,以免贻笑大方。当今流行于世的系统基本都会提供多线程这项基本功能,iOS也不例外。其中Swift提供了3种可选方案:NSThread,GCD和NSOperation,接下来我们将对3种方案进行运用和分析。
NSThread
NSThread是Objective-C给Swift留下的众多遗产之一,并且Swift给其定义了一个简体名称Thread。
1 class ViewController: UIViewController { 2 3 @IBOutlet weak var testLabel: UILabel! 4 var testThread:Thread? = nil 5 var count = 0 6 7 override func viewDidLoad() { 8 super.viewDidLoad() 9 testLabel.text = "Charpter9" 10 11 testThread = Thread.init(target: self, selector: #selector(threadFunc), object: "菜鸟先飞") 12 testThread!.start() 13 } 14 15 override func didReceiveMemoryWarning() { 16 super.didReceiveMemoryWarning() 17 // Dispose of any resources that can be recreated. 18 } 19 20 21 @objc func threadFunc(p: String) { 22 23 while(true) { 24 sleep(1) 25 count += 1 26 print("\(p): \(count)") 27 } 28 } 29 }
创建线程使用Thread.init(target:Any, selector: Selector, object: Any?)。
其中target表示selector函数所属的类,selector表示线程的函数名,object表示函数参数。
如上代码所示,当我们调用testFunc!.start()时,系统就会创建线程并执行threadFunc函数。
(注意gif右下角打印变化)
提示:针对target参数,网上有个千篇一律的说法是:“selector消息发送的对象”。针对这个解释,我只能说“非常忠于原文”,基本就是直译apple的文档。target其实就是selector函数所在的类实例,为什么要说的这么复杂呢?这是因为selector本身并非函数入口地址,而是1个字符串。线程启动时,selector字符串被交给了target,target调用和selector字符串同名的方法,进而启动线程,所以才有之前的那个晦涩的说法。
NSThread是1个轻量级线程调用(相对GCD和NSOperation而言),不带任何“赠品”。如果你要做数据同步,那你得自己加同步锁或信号量(NSLock和NSCondition);线程不用了要记得cancel掉,不然会内存泄漏。总之你得留个心眼儿好好管着NSThread这个熊孩子。
“什么?!内存泄漏?!NSThread太可怕了,又不好管,有没有安全听话一点的东东啊?”
GCD(Grand Central Dispatch)
这是iOS开发中出镜率最高的一种线程机制。如下图所示,
GCD涉及1个先入先出(FIFO)队列,任务依次入队,再依次出队交给线程执行。整个过程中,除了创建队列、新任务和加入任务到队列的动作外,其他均由系统自己处理。FIFO队列和线程的生老病死养老送终都由系统负责解决。真是名副其实的“大中央调度”。接下来我们看看GCD有哪些特性以及如何使用。
1 import UIKit 2 3 class ViewController: UIViewController { 4 5 var queue: DispatchQueue? 6 7 override func viewDidLoad() { 8 super.viewDidLoad() 9 /* create serial queue */ 10 queue = DispatchQueue.init(label: "com.ansersion.charpter9.queue.serial") 11 12 print("start test sync...") 13 for i in 1...3 { 14 print("sync start \(i)") 15 queue?.sync { 16 sleep(1) 17 let df = DateFormatter() 18 df.dateFormat = "HH:mm:ss" 19 print("sync executing:\(Thread.current),[\(df.string(from: Date()))]") 20 } 21 print("sync end \(i)") 22 } 23 print("start test async...") 24 for i in 1...3 { 25 print("async start \(i)") 26 queue?.async { 27 sleep(1) 28 let df = DateFormatter() 29 df.dateFormat = "HH:mm:ss" 30 print("async executing:\(Thread.current),[\(df.string(from: Date()))]") 31 } 32 print("async end \(i)") 33 } 34 35 } 36 37 override func didReceiveMemoryWarning() { 38 super.didReceiveMemoryWarning() 39 // Dispose of any resources that can be recreated. 40 } 41 42 43 }