在iOS开发中,为了合理利用CPU的多核,提高程序的执行效率 和 用户体验,我们需要使用多线程。
iOS中常用的多线程技术有:NSThread,GCD,NSOperation
1 NSThread:OC语言,面向对象,简单易用,可直接操作线程对象;
2 GCD:C语言,旨在替代NSThread,充分利用了CPU的多核,不需要考虑线程的生命周期;
3 NSOperation:OC语言,面向对象,基于GCD(底层是GCD),比GCD更加简单实用,也不需要考虑线程的生命周期。
首先研究下 NSThread
// 创建线程 01(通过创建线程对象创建线程)
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(eat:) object:@"apple"];
// 为线程明名
thread.name = @"eat-thread";
// 启动线程
[thread start];
注:一个NSThread对象就代表一条线程
// 创建线程 02(隐式创建并启动线程)
[self performSelectorInBackground:@selector(eat:) withObject:@"apple"];
// 创建线程 03 (类方法创建线程)
[NSThread detachNewThreadSelector:@selector(eat:) toTarget:self withObject:@"apple"];
- (void)eat:(NSString *)food {
NSLog(@"eat---%@---%@", food, [NSThread currentThread]);
}
// 让线程睡眠n秒(阻塞n秒)
[NSThread sleepForTimeInterval:n];
// 让线程睡眠到某一时刻,苏醒后继续执行
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
// 直接退出线程
[NSThread exit];
线程之间的通信
1、什么叫做线程间通信?
在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
2、线程间通信的体现
01: 一个线程传递数据给另一个线程
02 : 在一个线程中执行完特定任务后,转到另一个线程继续执行任务
线程间通信常用方法
// option 1
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// option 2
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
线程通信事例(利用子线程下载图片,回到主线程显示图片)
// 创建一个子线程,用来下载图片
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
// downloadImage function
- (void)downloadImage {
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://xxx.jpg"];
// 加载图片,并转为二进制文件(此方法耗内存和时间)
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程,显示图片 (与UI有关的操作,请放主线程)
// option-1
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
// option-2
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
// option-3
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}
// 设置图片的方法
-(void)showImage:(UIImage *)image {
self.imageView.image = image;
}
主线程相关用法
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程
线程安全 — 资源抢夺
一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
“比如多个线程访问同一个对象、同一个变量、同一个文件”
当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题
解决方案:使用互斥锁
互斥锁的使用前提:多条线程抢夺同一块资源
互斥锁使用格式
@synchronized(锁对象) {
// 需要锁定的代码
}
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源