概述

早上起床,你先打开洗衣机,然后用热水把泡面泡上,接着打开电脑开启一天的码农生活。其中“洗衣服”、“泡泡面”和“码代码”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右下角打印变化)

 

九、使用多线程——NSThread,GCD和NSOperation

提示:针对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开发中出镜率最高的一种线程机制。如下图所示,

九、使用多线程——NSThread,GCD和NSOperation

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 }
View Code

相关文章: