首页 > 代码库 > IOS多线程之NSThread

IOS多线程之NSThread

关于线程的概念不在赘述,网上讲的很详细,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 =http://www.mamicode.com/ [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 =http://www.mamicode.com/ [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

IOS多线程之NSThread