zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    使用Datagrip导入excel数据
    idea2020版本的lombok不能使用
    wait和notify
    死锁的原因
    synchronized关键字
    线程JOIN
    JSON解析精度丢失问题(net.sf.json)
    线程中断
    spring boot 2.0.0 + mybatis 报:Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required
    2、Dubbo源码解析--服务发布原理(Netty服务暴露)
  • 原文地址:https://www.cnblogs.com/zanglitao/p/4060643.html
Copyright © 2011-2022 走看看