在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案例,在实际使用中感受它们的区别。还有一点需要说明的是,这篇文章将会使用 Swift
和 Objective-c
两种语言讲解,双语幼儿园。OK,let's begin!
概述
这篇文章中,我不会说多线程是什么、线程和进程的区别、多线程有什么用,当然我也不会说什么是串行、什么是并行等问题,这些我们应该都知道的。
在 iOS 中其实目前有 4
套多线程方案,他们分别是:
- Pthreads
- NSThread
- GCD
- NSOperation & NSOperationQueue
所以接下来,我会一一讲解这些方案的使用方法和一些案例。在将这些内容的时候,我也会顺带说一些多线程周边产品。比如: 线程同步、 延时执行、 单例模式 等等。
Pthreads
其实这个方案不用说的,只是拿来充个数,为了让大家了解一下就好了。百度百科里是这么说的:
POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。
简单地说,这是一套在很多操作系统上都通用的多线程API,所以移植性很强(然并卵),当然在 iOS 中也是可以的。不过这是基于 c语言 的框架,使用起来这酸爽!感受一下:
OBJECTIVE-C
当然第一步要包含头文件
#import <pthread.h>
然后创建线程,并执行任务
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
pthread_t thread;
//创建一个线程并自动执行
pthread_create(&thread, NULL, start, NULL);
}
void *start(void *data) {
NSLog(@"%@", [NSThread currentThread]);
return NULL;
}
打印输出:
2015-07-27 23:57:21.689 testThread[10616:2644653] <NSThread: 0x7fbb48d33690>{number = 2, name = (null)}
看代码就会发现他需要 c语言函数,这是比较蛋疼的,更蛋疼的是你需要手动处理线程的各个状态的转换即管理生命周期,比如,这段代码虽然创建了一个线程,但并没有销毁。
SWIFT
很遗憾,在我目前的 swift1.2
中无法执行这套方法,原因是这个函数需要传入一个函数指针 CFunctionPointer<T>
类型,但是目前 swift 无法将方法转换成此类型。听说 swift 2.0
引入一个新特性 @convention(c)
, 可以完成 Swift 方法转换成 c 语言指针的。在这里可以看到
那么,Pthreads
方案的多线程我就介绍这么多,毕竟做 iOS 开发几乎不可能用到。但是如果你感兴趣的话,或者说想要自己实现一套多线程方案,从底层开始定制,那么可以去搜一下相关资料。
NSThread
这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 [NSThread currentThread]
,它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。下面来看看它的一些用法。
创建并启动
-
先创建线程类,再启动
OBJECTIVE-C
// 创建 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil]; // 启动 [thread start];
SWIFT
//创建 let thread = NSThread(target: self, selector: "run:", object: nil) //启动 thread.start()
-
创建并自动启动
OBJECTIVE-C
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
SWIFT
NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil)
-
使用 NSObject 的方法创建并自动启动
OBJECTIVE-C
[self performSelectorInBackground:@selector(run:) withObject:nil];
SWIFT
很遗憾 too! 苹果认为
performSelector:
不安全,所以在 Swift 去掉了这个方法。Note: The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.
其他方法
除了创建启动外,NSThread 还以很多方法,下面我列举一些常见的方法,当然我列举的并不完整,更多方法大家可以去类的定义里去看。
OBJECTIVE-C
//取消线程
- (void)cancel;
//启动线程
- (void)start;
//判断某个线程的状态的属性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//设置和获取线程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//获取当前线程信息
+ (NSThread *)currentThread;
//获取主线程信息
+ (NSThread *)mainThread;
//使当前线程暂停一段时间,或者暂停到某个时刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
SWIFT
Swift的方法名字和OC的方法名都一样,我就不浪费空间列举出来了。
其实,NSThread 用起来也挺简单的,因为它就那几种方法。同时,我们也只有在一些非常简单的场景才会用 NSThread, 毕竟它还不够智能,不能优雅地处理多线程中的其他高级概念。所以接下来要说的内容才是重点。
GCD
Grand Central Dispatch
,听名字就霸气。它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,我们只需要告诉干什么就行。同时它使用的也是 c语言
,不过由于使用了 Block(Swift里叫做闭包),使得使用起来更加方便,而且灵活。所以基本上大家都使用 GCD
这套方案,老少咸宜,实在是居家旅行、杀人灭口,必备良药。不好意思,有点中二,咱们继续。
任务和队列
在 GCD
中,加入了两个非常重要的概念: 任务 和 队列。
-
任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行 和 异步执行,他们之间的区别是
是否会创建新的线程
。同步执行:
只要是同步执行的任务,都会在当前线程执行,不会另开线程。异步执行:
只要是异步执行的任务,都会另开线程,在别的线程执行。更新:
这里说的并不准确,同步(sync)
和异步(async)
的主要区别在于会不会阻塞当前线程,直到Block
中的任务执行完毕!
如果是同步(sync)
操作,它会阻塞当前线程并等待