一. NSThread的创建方式
1. 类方法创建
类方法创建不需要手动开启线程
//detach派遣
[NSThread detachNewThreadSelector:@selector(threadMain1:) toTarget:selfwithObject:@(20)];
2. 实例方法创建
实例方法创建就要手动开启线程,否则不会调用threadMain2方法
NSThread *thread = [[NSThread alloc] initWithTarget:selfselector:@selector(threadMain2:) object:dict];
[thread start];
3. 隐式创建
隐式创建会自动启动,也不需要手动开启线程
[self performSelectorInbackground:@selector(download:) withObject:nil];
类方法创建和隐式创建的优缺点:
- 优点:简单快捷
- 缺点:无法对线程进行更详细的设置
二. 线程结束之后的通知
线程结束之后系统会发送通知,要先判断是哪个线程的通知,然后再进行操作。
[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(threadFinish:) name:NSThreadWillExitNotification object:nil];
要在threadFinish方法里面通过 NSThread *thread = [NSThread currentThread] 判断是哪个线程结束了,然后再进行操作。
三. 线程之间的通信
在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信。
线程间通信的体现:
- 一个线程传递数据给另一个线程
- 在一个线程中执行完特定任务后,转到另一个线程继续执行任务
线程间通信常用方法:
// 到主线程执行任务
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// 到指定线程执行任务
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 在子线程中调用download方法下载图片
[self performSelectorInBackground:@selector(download) withObject:nil];
示意图如下:

四. 线程相关方法
// 获取主线程
+ (NSThread *)mainThread;
// 是否为主线程
- (BOOL)isMainThread;
// 是否为主线程
+ (BOOL)isMainThread;
// 获得当前线程
NSThread *current = [NSThread currentThread];
// 线程的调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高
// 获取优先级
+ (double)threadPriority;
// 设置优先级
+ (BOOL)setThreadPriority:(double)p;
// 设置线程的名字
- (void)setName:(NSString *)n;
// 获取线程的名字
- (NSString *)name;
五. 线程的状态
1. 线程的创建
self.thread=[[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil];
// 创建线程有多种方式,这里不做过多的介绍。

2. 线程的开启
[self.thread start];

3. 线程的运行和阻塞
- 设置线程阻塞,阻塞2秒
[NSThread sleepForTimeInterval:2.0];
- 设置线程阻塞,传入一个date
NSDate *date=[NSDate dateWithTimeIntervalSinceNow:4.0];
[NSThread sleepUntilDate:date];

线程处理阻塞状态时在内存中的表现情况:线程被移出可调度线程池,此时不可调度。

4. 线程的死亡
当线程的任务结束,发生异常,或者是强制退出这三种情况会导致线程的死亡。

线程死亡后,线程对象从内存中移除。

结束线程:
[NSThread exit];
注意:人死不能复生,线程死了也不能复生(重新开启),如果在线程死亡之后,再次调用[self.thread start]尝试重新开启线程,则程序会挂,如下图:

六. 线程相关练习
1. 小练习1
线程1一直运行,线程2打印,线程2结束之后通知线程1结束,线程1结束之后播放音效。
#import "ViewController.h"
#import <AudioToolbox/AudioToolbox.h> //需要导入audiotoolbox框架
@interface ViewController ()
{
NSLock *threadLock;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
threadLock = [[NSLock alloc]init];
//线程结束之后的通知 系统自己发送的通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadFinish:) name:NSThreadWillExitNotification object:nil];
}
- (void)pressBtn:(UIButton *)button
{
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain1:) object:nil];
thread.name = @"线程1";
[thread start];
NSDictionary *dict = @{@"thread":thread,@"num":@(100)};
NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:dict];
thread2.name = @"线程2";
[thread2 start];
}
- (void)threadMain1:(id)num
{
[threadLock lock];
int i = 0;
while (1) {
NSLog(@"线程1 = %d",i ++);
[NSThread sleepForTimeInterval:1];
//判断是否能接收到cancel消息接收到消息就从cpu中消失
if ([[NSThread currentThread] isCancelled]) {
NSLog(@"线程1接收到cancel消息");
[NSThread exit];
}
}
[threadLock unlock];
}
- (void)threadMain2:(NSDictionary *)dict
{
[threadLock lock];
NSLog(@"%@开始",[[NSThread currentThread] name]);
for (int i =0; i <[dict[@"num"] integerValue]; i ++) {
NSLog(@"线程2 = %d",i +1);
[NSThread sleepForTimeInterval:0.1];
}
NSLog(@"线程2结束");
//线程2结束让线程1立即停止
[dict[@"thread"] cancel];
[threadLock unlock];
}
- (void)threadFinish:(NSNotification *)notify
{
NSThread *thread = [NSThread currentThread];
NSLog(@"线程%@结束",thread.name);
if thread.name = @"线程2" {
//添加线程结束通知音
NSString *path = [[NSBundle mainBundle]pathForResource:@"sound" ofType:@"wav"];
NSURL *url = [NSURL fileURLWithPath:path];
SystemSoundID SID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &SID);
AudioServicesPlaySystemSound(SID);
}
}
2. 小练习2
一般情况下主线程处理UI操作,子线程处理一些耗时的操作,为了防止一些耗时的操作卡死主线程,我们可以在子线程中处理耗时的操作,然后通过performSelectorOnMainThread回到主线程刷新UI。
#import "XLViewController.h"
@interface XLViewController ()
{
UIProgressView * progressView;
}
@end
@implementation XLViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self createUIButton];
progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(10, 50, self.view.frame.size.width - 20, 10)];
progressView.progress = 0;
[self.view addSubview:progressView];
}
-(void)createUIButton
{
UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(100, 100, 100, 100);
[btn setTitle:@"线程" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[btn addTarget:self action:@selector(pressBtn:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
-(void)pressBtn:(id)sender
{
// 创建子线程,防止主线程出现假死的情况
[NSThread detachNewThreadSelector:@selector(threadMain:) toTarget:self withObject:@(100)];
}
-(void)threadMain:(NSNumber *)num
{
for(int i = 0;i<=num.intValue;i++)
{
// progressView.progress = i / num.floatValue;
// NSLog(@"%f",progressView.progress);
// 回到主线程刷新UI
[self performSelectorOnMainThread:@selector(changeProgressView:) withObject:@(i) waitUntilDone:NO];
[NSThread sleepForTimeInterval:0.1];
}
}
-(void)changeProgressView:(NSNumber *)num
{
progressView.progress = num.floatValue / 100;
NSLog(@"%f",progressView.progress);
}
@end
网友评论