zoukankan      html  css  js  c++  java
  • ios 多线程-GCD-NSOperation

    一、线程间的通讯

    1、使用NSObject类的方法performSelectorInBackground:withObject:来创建一个线程。

    具体的代码:隐式创建,自动启动

    [Object performSelectorInBackground:@selector(doSomething:) withObject:nil];

    2、选择使用NSThread实现多线程。

    NSThread创建主要有两种方式:

    (1):创建完自动启动

    [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];

    (2):创建后,需要手动启动

    NSThread* myThread = [[NSThread alloc] initWithTarget:self   selector:@selector(doSomething:) object:nil];

    [myThread start];

    这两种方式的区别在于:

    前一种调用就会立即创建一个线程并执行selector方法;第二种方式尽管alloc了一个新Thread,但需要手动调用start方法来启动线程。这点与Java创建线程的方式相似。

    第一种方式,与上述做法1使用NSObject的类方法performSelectorInBackground:withObject:是一样的;第二种方式的可以在start真正创建线程之前对其进行设置,比如设置线程的优先级。

    注意:

    - (void) doSomething:(id)sender

    {

        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        //执行你的代码

       [pool release];

    [self performSelectorOnMainThread:@selector(doSomethingFinished:) withObject:image waitUntilDone:NO];

    }

    在多线程的执行方法doSomething中需要自行管理内存的释放,否则可能会警告提示:

    XXXXX nsthread autoreleased with no pool in place – just leaking


    performSelectorOnMainThread回到主线程(更新UI的话必须到主线程),用或者调用或者调用 委托函数,在主线程中实现委托函数

     

    3.常见方法

     

    1> 获得当前线程

     

    + (NSThread *)currentThread;

     

     

     

    2> 获得主线程

     

    + (NSThread *)mainThread;

     

     

     

    3> 睡眠(暂停)线程

     

    + (void)sleepUntilDate:(NSDate *)date;

     

    + (void)sleepForTimeInterval:(NSTimeInterval)ti;

     

     

     

    4> 设置线程的名字

     

    - (void)setName:(NSString *)n;

     

    - (NSString *)name;

     

     

    注意点:

    1.不要同时开太多的线程(1~3条线程即可,不要超过5条)

     

    2.线程概念

     

    1> 主线程 : UI线程,显示、刷新UI界面,处理UI控件的事件

     

    2> 子线程 : 后台线程,异步线程

     

    3.不要把耗时的操作放在主线程,要放在子线程中执行

     

     

    二、GCD

    0.基本使用

        // dispatch_sync : 同步,不具备开启线程的能力
        // dispatch_async : 异步,具备开启线程的能力
        
        // 并发队列 :多个任务可以同时执行
        // 串行队列 :一个任务执行完后,再执行下一个任务
        
        // 创建全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        // 创建一个串行队列
        dispatch_queue_t queue = dispatch_queue_create("cn.heima.queue", NULL);   

     

    1.队列和任务

     

    1> 任务 :需要执行什么操作

     

    * 用block来封装任务

     

     

     

    2> 队列 :存放任务

     

    * 全局的并发队列 : 可以让任务并发执行

     

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

     

     

     

    * 自己创建的串行队列 : 让任务一个接着一个执行

     

    dispatch_queue_t queue = dispatch_queue_create("cn.heima.queue", NULL);

     

     

     

    * 主队列 : 让任务在主线程执行

     

    dispatch_queue_t queue = dispatch_get_main_queue();

     

     

     

    2.执行任务的函数

     

    1> 同步执行 : 不具备开启新线程的能力

     

    dispatch_sync...

     

     

     

    2> 异步执行 : 具备开启新线程的能力

     

    dispatch_async...

     

     

     

    3.常见的组合(掌握)

     

    1> dispatch_async + 全局并发队列(最常用)

     *  会不会创建线程:会,一般同时开多条
     *  任务的执行方式:并发执行

     

     

    2> dispatch_async + 自己创建的串行队列

     *  会不会创建线程:会,一般只开1条线程
     *  任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)

     

        // 若是非ARC,需要释放创建的队列
    //    dispatch_release(queue); 

     

    3>  dispatch_async + 让任务在主线程执行(常用)

    添加到主队列中的任务,都会自动放到主线程中去执行

     

    4>  dispatch_sync + 让任务在主线程执行(不能用---会卡死)

     

    5> dispatch_sync + 全局并发队列

     *  会不会创建线程:不会
     *  任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)
     *  并发队列失去了并发的功能

     

    6> dispatch_sync + 自己创建的串行队列

     *  会不会创建线程:不会
     *  任务的执行方式:串行执行(一个任务执行完毕后再执行下一个任务)

     

     

     

    4.线程间的通信(掌握)

     

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

     

       // 执行耗时的异步操作...

     

       

     

       

     

       dispatch_async(dispatch_get_main_queue(), ^{

     

           // 回到主线程,执行UI刷新操作

     

       });

     

    });

     

     

     

    5.GCD的所有API都在libdispatch.dylib,Xcode会自动导入这个库

     

    * 主头文件 : #import <dispatch/dispatch.h>

     

     

     

    6.延迟执行(掌握)

     

    1> performSelector....

     // 一旦定制好延迟任务后,不会卡主当前线程

    // 3秒后自动回到当前线程调用self的download:方法,并且传递参数:@"http://555.jpg"

     

    [self performSelector:@selector(download:) withObject:@"http://555.jpg" afterDelay:3];

     

     

     

    2> dispatch_after...

     

    // 任务放到哪个队列中执行

     

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

     

    double delay = 3; // 延迟多少秒

     

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{

     

        // 3秒后需要执行的任务

     

    });

     

     

     

    7.一次性代码(掌握)

     

    static dispatch_once_t onceToken;

     

    dispatch_once(&onceToken, ^{

     

        // 这里面的代码,在程序运行过程中,永远只会执行1次

     

    });

     

    8、例子

    #define HMGlobalQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    #define HMMainQueue dispatch_get_main_queue()

     // 1.子线程下载图片

    dispatch_async(HMGlobalQueue, ^{
            NSLog(@"donwload---%@", [NSThread currentThread]);
            NSURL *url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/pic/item/37d3d539b6003af3290eaf5d362ac65c1038b652.jpg"];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];       
    // 2.回到主线程设置图片
            dispatch_async(HMMainQueue, ^{
                NSLog(@"setting---%@ %@", [NSThread currentThread], image);
                [self.button setImage:image forState:UIControlStateNormal];
            });
        });

     

    9、队列组

    ①、创建队列组于任务

        // 队列组
        dispatch_group_t group = dispatch_group_create();
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

     

    ②、在队列组内,扔就任务

        // 下载图片1
        __block UIImage *image1 = nil;
        dispatch_group_async(group, queue, ^{
            NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
            NSData *data1 = [NSData dataWithContentsOfURL:url1];
            image1 = [UIImage imageWithData:data1];
        });
        
        // 下载图片2
        __block UIImage *image2 = nil;
        dispatch_group_async(group, queue, ^{
            NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
            NSData *data2 = [NSData dataWithContentsOfURL:url2];
            image2 = [UIImage imageWithData:data2];
        });

     

    ③、保证执行完组里面的所有任务之后,再执行notify函数里面的block

    dispatch_group_notify(group, queue, ^{。。。。。。}

    三、NSOperation和NSOperationQueue

    1.队列的类型

    1> 主队列

    * [NSOperationQueue mainQueue]

    * 添加到"主队列"中的操作,都会放到主线程中执行

     

    2> 非主队列

    * [[NSOperationQueue alloc] init]

    * 添加到"非主队列"中的操作,都会放到子线程中执行

     

    2.队列添加任务

    * - (void)addOperation:(NSOperation *)op;

    * - (void)addOperationWithBlock:(void (^)(void))block;

    代码示例:

        // 创建一个队列(非主队列)
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        // 添加操作到队列中(自动异步执行任务,并发)
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载图片1---%@", [NSThread currentThread]);
        }];
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载图片2---%@", [NSThread currentThread]);
        }];
        
        [queue addOperation:operation1];
        [queue addOperation:operation2];
        [queue addOperationWithBlock:^{
            NSLog(@"下载图片3---%@", [NSThread currentThread]);
        }];// 3个操作并发执行

     

    3.常见用法

    1> 设置最大并发数

    - (NSInteger)maxConcurrentOperationCount;

    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

    代码示例:

    - (void)maxCount
    {
        // 1.创建一个队列(非主队列)
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        // 2.设置最大并发(最多同时并发执行3个任务)
        queue.maxConcurrentOperationCount = 3;
        
        // 3.添加操作到队列中(自动异步执行任务,并发)
        NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载图片1---%@", [NSThread currentThread]);
        }];
        NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载图片2---%@", [NSThread currentThread]);
        }];
        NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载图片3---%@", [NSThread currentThread]);
        }];
        NSBlockOperation *operation4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载图片4---%@", [NSThread currentThread]);
        }];


        NSInvocationOperation *operation5 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download) object:nil];
        

        // operation直接调用start,是同步执行(在当前线程执行操作)
    //    [operation start];

     

    // 添加操作到队列中,会自动异步执行
        [queue addOperation:operation1];
        [queue addOperation:operation2];
        [queue addOperation:operation3];
        [queue addOperation:operation4];
        [queue addOperation:operation5];
        [queue addOperationWithBlock:^{
            NSLog(@"下载图片5---%@", [NSThread currentThread]);
        }];

        [queue cancelAllOperations];
    }

    - (void)download
    {
        NSLog(@"download-----%@", [NSThread currentThread]);
    }

     

    2> 队列的其他操作

    - (void)didReceiveMemoryWarning
    {
        [super didReceiveMemoryWarning];
        
    //    [queue cancelAllOperations]; // 取消队列中的所有任务(不可恢复)
    }

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
    //    [queue setSuspended:YES]; // 暂停队列中的所有任务
    }

    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
    //    [queue setSuspended:NO]; // 恢复队列中的所有任务
    }

     

    * 取消所有的操作

    - (void)cancelAllOperations;

     

    * 暂停所有的操作

    [queue setSuspended:YES];

     

    * 恢复所有的操作

    [queue setSuspended:NO];

     

    4.操作之间的依赖(面试题)

    * NSOperation之间可以设置依赖来保证执行顺序

    * [operationB addDependency:operationA];

    // 操作B依赖于操作A,等操作A执行完毕后,才会执行操作B

    * 注意:不能相互依赖,比如A依赖B,B依赖A

    * 可以在不同queue的NSOperation之间创建依赖关系

     

    5.线程之间的通信

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    [queue addOperationWithBlock:^{

        // 1.执行一些比较耗时的操作

       

        // 2.回到主线程

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

           

        }];

    }];

     

    四、从其他线程回到主线程的方式

    1.perform...

    [self performSelectorOnMainThread:<#(SEL)#> withObject:<#(id)#> waitUntilDone:<#(BOOL)#>];

     

    2.GCD

    dispatch_async(dispatch_get_main_queue(), ^{

     

    });

     

    3.NSOperationQueue

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

       

    }];

     
     
     
     
     
  • 相关阅读:
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    FFmpeg解码H264及swscale缩放详解
    linux中cat more less head tail 命令区别
    C语言字符串操作总结大全(超详细)
    如何使用eclipse进行嵌入式Linux的开发
  • 原文地址:https://www.cnblogs.com/zhongxuan/p/4852812.html
Copyright © 2011-2022 走看看