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 = [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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。