关于线程的概念不在赘述,网上讲的很详细,IOS中主要提供了3种方式实现多线程,分别是NSThread,NSOperation以及GCD,这里我们总结下最基础的NSThread
1 线程创建
可以使用NSthread提供的方法创建一个新的线程,创建方法有如下两种
a.+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
b.- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
前一种方式创建了一个新的线程并立马执行线程任务,后一种方式创建了一个新的线程,方法返回NSThread的实例,当调用- (void)start时才会执行线程任务
多线程常用的使用场景是当一些耗时的操作(比如下载)运行在主线程时,主线程会被阻塞住,给用户一种"卡住"的感觉,所以我们需要把这些耗时的操作放到子线程中,主线程只处理UI操作以及一些时间代价下的操作。
- (void)viewDidLoad { [super viewDidLoad]; [NSThread detachNewThreadSelector:@selector(doInMutableThread) toTarget:self withObject:nil]; } - (void)doInMutableThread { [NSThread sleepForTimeInterval:3]; NSLog(@"%@",[NSThread currentThread]); }
上面代码中sleepForTimeInterval使执行此函数的线程睡眠3秒用来模拟耗时操作,3秒后再继续执行后面的代码
下面再来看一个完整的例子,当点击下载按钮后,开启8个子线程下载图片,下载完成后通过方法performSelectorOnMainThread回到主线程更新UI(IOS中UI相关的操作必须执行在主线程中)
#import "ZLTViewController.h" @interface ZLTViewController () { UIImageView *_imageView1; UIImageView *_imageView2; UIImageView *_imageView3; UIImageView *_imageView4; UIImageView *_imageView5; UIImageView *_imageView6; UIImageView *_imageView7; UIImageView *_imageView8; UIButton *_loadButton; NSArray *_imageUrlArray; } @end @implementation ZLTViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self setUpView]; } - (void)setUpView { _imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 120, 100)]; _imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 130, 120, 100)]; _imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 240, 120, 100)]; _imageView4 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 350, 120, 100)]; _imageView5 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 20, 120, 100)]; _imageView6 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 130, 120, 100)]; _imageView7 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 240, 120, 100)]; _imageView8 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 350, 120, 100)]; _imageUrlArray = @[@"http://www.iyi8.com/uploadfile/2014/1002/20141002112239407.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112240514.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112248343.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112236540.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112242679.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112243630.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112245663.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112247219.jpg"]; _loadButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; _loadButton.frame = CGRectMake(120, 490, 80, 30); [_loadButton setTitle:@"下载" forState:UIControlStateNormal]; [_loadButton addTarget:self action:@selector(downImage) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:_imageView1]; [self.view addSubview:_imageView2]; [self.view addSubview:_imageView3]; [self.view addSubview:_imageView4]; [self.view addSubview:_imageView5]; [self.view addSubview:_imageView6]; [self.view addSubview:_imageView7]; [self.view addSubview:_imageView8]; [self.view addSubview:_loadButton]; } - (void)downImage { for (int i = 0; i < _imageUrlArray.count; i++) { //[NSThread detachNewThreadSelector:@selector(loadImage:) toTarget:self withObject:[NSNumber numberWithInt:i]]; NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]]; thread.name = [NSString stringWithFormat:@"thread%d",i]; [thread start]; } } - (void)loadImage:(NSNumber *)index { @autoreleasepool { NSLog(@"%@",[NSThread currentThread]); int i = [index intValue]; NSString *urlStr = _imageUrlArray[i]; NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlStr]]; UIImage *image = [UIImage imageWithData:data]; NSDictionary *dic = @{@"index":index,@"image":image}; [self performSelectorOnMainThread:@selector(updateImageView:) withObject:dic waitUntilDone:YES]; } } - (void)updateImageView:(NSDictionary *)dic { switch ([dic[@"index"] intValue]) { case 0: _imageView1.image = dic[@"image"]; break; case 1: _imageView2.image = dic[@"image"]; break; case 2: _imageView3.image = dic[@"image"]; break; case 3: _imageView4.image = dic[@"image"]; break; case 4: _imageView5.image = dic[@"image"]; break; case 5: _imageView6.image = dic[@"image"]; break; case 6: _imageView7.image = dic[@"image"]; break; case 7: _imageView8.image = dic[@"image"]; break; } } @end
上面的例子中我把子线程方法放在了自动释放池中,那是因为在消息循环中系统会自动为主线程创建自动释放池,但不会为子线程创建自动释放池,所以需要我们手动创建
2 线程状态
线程有3种状态:执行中(isExecuting),执行结束(isFinished),取消执行(isCancelled)
我们可以向线程发送消息cancel取消线程,但是cancel仅仅使线程isCancelled返回YES而已,我们需要在代码中判断线程的状态,并调用exit终结当前线程。
给上面程序加上停止按钮,通过设置线程状态停止图片加载。
// // ZLTViewController.m // IOS多线程 // // Created by user on 14-10-28. // Copyright (c) 2014年 臧立涛. All rights reserved. // #import "ZLTViewController.h" @interface ZLTViewController () { UIImageView *_imageView1; UIImageView *_imageView2; UIImageView *_imageView3; UIImageView *_imageView4; UIImageView *_imageView5; UIImageView *_imageView6; UIImageView *_imageView7; UIImageView *_imageView8; UIButton *_loadButton; NSArray *_imageUrlArray; //线程数组 NSMutableArray *_threadArray; UIButton *_stopButton; } @end @implementation ZLTViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self setUpView]; } - (void)setUpView { _imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 120, 100)]; _imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 130, 120, 100)]; _imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 240, 120, 100)]; _imageView4 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 350, 120, 100)]; _imageView5 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 20, 120, 100)]; _imageView6 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 130, 120, 100)]; _imageView7 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 240, 120, 100)]; _imageView8 = [[UIImageView alloc] initWithFrame:CGRectMake(170, 350, 120, 100)]; _imageUrlArray = @[@"http://www.iyi8.com/uploadfile/2014/1002/20141002112239407.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112240514.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112248343.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112236540.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112242679.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112243630.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112245663.jpg", @"http://www.iyi8.com/uploadfile/2014/1002/20141002112247219.jpg"]; _threadArray = [NSMutableArray array]; _loadButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; _loadButton.frame = CGRectMake(80, 490, 80, 30); [_loadButton setTitle:@"下载" forState:UIControlStateNormal]; [_loadButton addTarget:self action:@selector(downImage) forControlEvents:UIControlEventTouchUpInside]; _stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; _stopButton.frame = CGRectMake(160, 490, 80, 30); [_stopButton setTitle:@"停止" forState:UIControlStateNormal]; [_stopButton addTarget:self action:@selector(stopDownImage) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:_imageView1]; [self.view addSubview:_imageView2]; [self.view addSubview:_imageView3]; [self.view addSubview:_imageView4]; [self.view addSubview:_imageView5]; [self.view addSubview:_imageView6]; [self.view addSubview:_imageView7]; [self.view addSubview:_imageView8]; [self.view addSubview:_loadButton]; [self.view addSubview:_stopButton]; } - (void)stopDownImage { for (int i = 0; i < _threadArray.count; i++) { //如果线程未结束则停止 if (![_threadArray[i] isFinished]) { [_threadArray[i] cancel]; } } } - (void)downImage { for (int i = 0; i < _imageUrlArray.count; i++) { //[NSThread detachNewThreadSelector:@selector(loadImage:) toTarget:self withObject:[NSNumber numberWithInt:i]]; NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImage:) object:[NSNumber numberWithInt:i]]; thread.name = [NSString stringWithFormat:@"thread%d",i]; [_threadArray addObject:thread]; [thread start]; } } - (void)loadImage:(NSNumber *)index { @autoreleasepool { int i = [index intValue]; NSString *urlStr = _imageUrlArray[i]; NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlStr]]; NSThread *thread = [NSThread currentThread]; if ([thread isCancelled]) { //取消当前线程 [NSThread exit]; } UIImage *image = [UIImage imageWithData:data]; NSDictionary *dic = @{@"index":index,@"image":image}; [self performSelectorOnMainThread:@selector(updateImageView:) withObject:dic waitUntilDone:YES]; } } - (void)updateImageView:(NSDictionary *)dic { switch ([dic[@"index"] intValue]) { case 0: _imageView1.image = dic[@"image"]; break; case 1: _imageView2.image = dic[@"image"]; break; case 2: _imageView3.image = dic[@"image"]; break; case 3: _imageView4.image = dic[@"image"]; break; case 4: _imageView5.image = dic[@"image"]; break; case 5: _imageView6.image = dic[@"image"]; break; case 6: _imageView7.image = dic[@"image"]; break; case 7: _imageView8.image = dic[@"image"]; break; } } @end
3 线程优先级
我们可以通过threadPriority获取或者设置线程的优先级,这个属性的取值范围是0至1,数字越大表示优先级越高,越有可能获得调度,创建线程默认的优先级为0.5
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImage) object:nil]; thread.name = @"thread"; //获取线程优先级 [thread threadPriority]; //设置线程优先级 thread.threadPriority = 0.5;
4 分类扩展
NSObject提供了方法用来更方便的实现多线程,这些方法使用的就是NSThread
@interface NSObject (NSThreadPerformAdditions)
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
@end