一、iOS多线程
iOS多线程开发有三种方式:
1. NSThread
2. NSOperation
3. GCD
iOS在每个进程启动后都会创建一个主线程,更新UI要在主线程上,所以也称为UI线程,是其他线程的父线程。
线程和进程的区:
线程(thread):用于指代独立执行的代码段。
进程(process):用于指代一个正在运行的可执行程序,它可以包含多个线程。
二、NSThread
NSThreadhi轻量级的多线程开发,需要自己管理线程生命周期
创建线程主要实现方法:
/* 直接将操作添加到新线程中并执行,该方法无法拿到线程对象 */
+ (void)detachNewThreadSelector:(SEL)selector /* 方法名 */
toTarget:(id)target /* 调用对象 */
withObject:(id)argument; /* 参数 */
/* 创建线程对象,初始化线程任务,调用start方法启动线程 */
- (instancetype)initWithTarget:(id)target /* 调用对象 */
selector:(SEL)selector /* 方法名 */
object:(id)argument;/* 参数 */
使用
/* 创建一个线程,初始化任务,创建线程并不会启动线程 */
NSThread *thread = [[NSThread alloc] initWithTarget:self
selector:@selector(loadImage)
object:nil];
[thread start];//启动线程
/* 直接将操作添加到新线程并启动线程 */
[NSThread detachNewThreadSelector:@selector(loadImage)
toTarget:self
withObject:nil];
下面是常用方法来控制线程:
/* 让线程休眠 */
+ (void)sleepUntilDate:(NSDate *)date;/* 让当前执行线程休眠到某个时间 */
+ (void)sleepForTimeInterval:(NSTimeInterval)time;/* 让当前执行线程休眠固定多少秒 */
/* 终止线程 */
+ (void)exit;
/* 停止线程,注意在主线程中调用仅仅只是设置线程状态,不会立刻停止线程 */
- (void)cancel;
三、NSOperation
NSOperation是个基类,我们直接使用的是它的子类:
1. NSInvocationOperation:调用方法SEL的方式执行线程可以使用它
2. NSBlockOperation:调用Block的方式执行线程可以使用它
实例
//创建Invocation线程
NSInvocationOperation *invocationOperation =
[[NSInvocationOperation alloc] initWithTarget:self
selector:@selector(loadImage)
object:nil];
//注意如果直接调用start方法,则此操作会在主线程中调用
// [invocationOperation start];//一般不会这么操作,而是添加到NSOperationQueue中
//创建线程队列
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
operationQueue.maxConcurrentOperationCount = 5;//设置最大并发线程数
//注意添加到线程队列后,队列里的线程就会开始执行
[operationQueue addOperation:invocationOperation];
//创建Block线程
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
[self loadImage:[NSNumber numberWithInt:0]];
}];
//添加进线程队列
[operationQueue addOperation:blockOperation];
四、 GCD
任务和队列
在 GCD 中,加入了两个非常重要的概念: 任务 和 队列。
1. 任务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个 Block,所以添加任务十分方便。任务有两种执行方式: 同步执行 和 异步执行,他们之间的区别是 是否会创建新的线程。
2. 队列:用于存放任务。一共有两种队列, 串行队列 和 并行队列。
串行队列和并发队列的创建:
/*创建一个队列
第一个参数:队列名称
第二个参数:队列类型,DISPATCH_QUEUE_SERIAL串行,DISPATCH_QUEUE_CONCURRENT并发
注意:GCD的queue不是指针类型
*/
dispatch_queue_t serialQueue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
/* 使用dispatch_get_global_queue() 方法取得一个全局的并发队列 */
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
队列的异步执行和同步执行:
//异步执行队列任务,第一个参数是队列,第二个参数是任务Block
dispatch_async(queue, ^{
[self loadImage:[NSNumber numberWithInt:i]];
});
//同步执行队列任务,第一个参数是队列,第二个参数是任务Block
dispatch_sync(queue, ^{
[self loadImage:[NSNumber numberWithInt:i]];
});
GCD的其他任务执行方法:
/* 重复执行某个任务,为了不阻塞线程可以使用dispatch_async()包装一下再执行 */
dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
/* 单次执行一个任务,此方法中的任务只会执行一次,重复调用也没办法重复执行(单例模式中常用此方法)*/
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
/* 常用:延迟delayInSeconds秒后在队列queue执行block操作 */
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_queue_t queue, dispatch_block_t block);
五、线程同步
为什么需要线程同步?因为要解决多线程的资源抢夺的问题
//初始化锁对象
NSLock *myLock = [[NSLock alloc] init];
//加锁
[myLock lock];//加锁后,下面的代码只能有一个线程进入执行
if (_imageNames.count > 0) {
name = [_imageNames lastObject];
[_imageNames removeObject:name];
}
//使用完解锁
[myLock unlock];