• iOS开发网络多线程之多线程


    一. 基本概念

    1. 进程

        进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内

        1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。

    2. 进程中的线程运行状态

    1> 单线程: 串行执行任务

        1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。

    2> 多线程: 并行执行任务

        1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。


    注:多线程并发执行任务的原理

        在同一时间里,CPU只能处理1条线程,只有1条线程在工作(执行)。多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象


    3. 多线程的优缺点

    • 优点

        1)能适当提高程序的执行效率。

        2)能适当提高资源利用率(CPU、内存利用率)

    • 缺点

        1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。

        2)线程越多,CPU在调度线程上的开销就越大。

        3)程序设计更加复杂:比如线程之间的通信、多线程的数据共享


    4. 多线程在iOS开发中应用

    • 4.1 主线程

        1)一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。

        2)作用。刷新显示UI,处理UI事件。

    • 4.2 使用注意

        1)不要将耗时操作放到主线程中去处理,会卡住线程。

        2)和UI相关的刷新操作必须放到主线程中进行处理。


    5. iOS中多线程的实现方法

    • 5.1 pthread

    特点:

        1)一套通用的多线程API

        2)适用于UnixLinuxWindows等系统

        3)跨平台可移植

        4)使用难度大

    使用语言:c语言

    使用频率:几乎不用

    线程生命周期:由程序员进行管理


    • 5.2 NSThread

    特点:

        1)使用更加面向对象

        2)简单易用,可直接操作线程对象

    使用语言:OC语言

    使用频率:偶尔使用

    线程生命周期:由程序员进行管理


    • 5.3 GCD

    特点:

        1)旨在替代NSThread等线程技术

        2)充分利用设备的多核(自动)

    使用语言:C语言

    使用频率:经常使用

    线程生命周期:自动管理


    • 5.4 NSOperation

    特点:

        1)基于GCD(底层是GCD)

        2)比GCD多了一些更简单实用的功能

        3)使用更加面向对象

    使用语言:OC语言

    使用频率:经常使用

    线程生命周期:自动管理



    二. 线程详解


    1. PThread创建线程

    第一个参数:线程对象地址

    第二个参数:线程属性

    第三个参数:指向函数的指针

    ​第四个参数:传递给该函数的参数

    1. // 1.PThread线程创建
    2. - (void)pthread
    3. {
    4.    pthread_t pthread;
    5.    
    6.    // 1.用pthead创建线程
    7.    pthread_create(&pthread, nil, run, nil);
    8. }
    9. void *run(void *param)
    10. {
    11.    NSLog(@"%s", __func__);
    12.    NSLog(@"%@", [NSThread currentThread]);
    13.    return nil;
    14. }


    2.NSThread创建线程


    1> 通过alloc创建

    1. // NSThread线程的创建 : 方法一
    2. /*
    3. 通过alloc创建可以拿到线程对象,需要手动启动线程
    4. 线程执行完之后,系统自动销毁
    5. */
    6. - (void)thread1
    7. {
    8.    // 方法一 :
    9.    NSThread *thread = [[LDThread alloc] initWithTarget:self selector:@selector(running) object:nil];
    10.    
    11.    thread.name = @"thread";
    12.    
    13.    [thread start];
    14. }

    2> 分离出一条子线程

    1. // NSThread线程的创建 : 方法二 : 分离出一条子线程
    2. - (void)thread2
    3. {
    4.    // 方法二 :
    5.    [NSThread detachNewThreadSelector:@selector(running) toTarget:self withObject:nil];
    6. }

    3> 创建一条后台子线程

    1. // NSThread线程的创建 : 方法三 : 创建一条后台子线程
    2. - (void)thread3
    3. {
    4.    [self performSelectorInBackground:@selector(running) withObject:nil];
    5. }


    4> NSThread线程安全问题

        对于线程访问公有的资源会引起数据混乱

        解决方案:加锁, 当前线程访问时,将访问资源加锁,其他线程在外等待,等待当前线程访问完之后再访问之后再加锁

        锁必须是一把是公有的,建议直接使用当前控制器

        案例:卖票,有3条线程相当于售票窗口,共同卖100张票,直到票卖完为止,如按普通开启3条线程,如当前票是100张,售票窗口A先去查看当前余票是100张,它拿出卖出一张,再把剩余99张票数放到数据库,而当A取出票的同时B也在取看到的余票也是100,卖出一张把自己计算出的剩余票99张放到数据库中把A计算的余票覆盖掉,这样卖出了2张票,而余票还是99张就造成了数据的混乱

        解决方案就是:将访问公共资源时加锁

        代码如下:

    • 创建3条线程

    1. - (void)viewDidLoad {
    2.    [super viewDidLoad];
    3.    
    4.    self.ticketCount = 100;
    5.    
    6.    // 线程1
    7.    NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
    8.    threadA.name = @"售票员A";
    9.    self.threadA = threadA;
    10.    [threadA start];
    11.    
    12.    // 线程2
    13.    NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
    14.    threadB.name = @"售票员B";
    15.    self.threadB = threadB;
    16.    [threadB start];
    17.    
    18.    NSThread *threadC = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
    19.    threadC.name = @"售票员C";
    20.    self.threadC = threadC;
    21.    [threadC start];
    22.    
    23. }
    • 加锁方案 用 @synchronized(self){}将访问公共的资源包装起来

    1. - (void)sellTicket
    2. {
    3.    while (1) {
    4.        
    5.        // 对于线程访问公有的资源为防止数据混乱
    6.        // 解决方案:加锁, 当前线程访问时,将访问资源加锁,其他线程在外等待,等待当前线程访问完之后再访问之后再加锁
    7.        // 锁必须是一把是公有的,建议直接使用当前控制器
    8.        @synchronized(self) {
    9.            
    10.        NSInteger count = self.ticketCount;
    11.    
    12.        if (count > 0) {
    13.            self.ticketCount = count - 1;
    14.            [NSThread sleepForTimeInterval:0.1];
    15.            NSLog(@"%@卖了一张票, 还剩%zd张票, 线程%@", [NSThread currentThread].name, self.ticketCount, [NSThread currentThread]);
    16.        } else {
    17.            NSLog(@"%@已卖完票", [NSThread currentThread].name);
    18.            break;
    19. //            [NSThread exit];
    20.        }
    21.            
    22.        }
    23.        
    24.    }
    25. }

    3. NSThread线程间的通信(下载图片)

    1> 点击按钮开启一条线程

    1. - (IBAction)btnClick {
    2.    
    3.    // 开启一条线程
    4.    [NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];
    5.    
    6. }


    2> 实现线程方法

        下载图片是耗时任务,放到子线程中执行,刷新图片放在主线程中执行

    1. // 下载图片
    2. - (void)download
    3. {
    4. //    NSLog(@"%@", [NSDate date]);
    5.    // 1.创建URL路径
    6.    NSURL *url = [NSURL URLWithString:@"http://src.house.sina.com.cn/imp/imp/deal/3f/e1/d/2c0401b364ae649b34e7d6999e4_p24_mk24_wm200_s500X0.png"];
    7.    
    8. //    NSLog(@"%@", [NSDate date]);
    9.    
    10.    // 执行开始时间
    11.    CFTimeInterval start = CFAbsoluteTimeGetCurrent();
    12.    
    13.    // 2.加载二进制
    14.    NSData *data = [NSData dataWithContentsOfURL:url];
    15.    
    16.    // 结束执行时间
    17.    CFTimeInterval end = CFAbsoluteTimeGetCurrent();
    18.    
    19.    NSLog(@"消耗 %f s", end - start);
    20.    
    21. //    NSLog(@"%@", [NSDate date]);
    22.    
    23.    // 3.将二进制转为图片
    24.    UIImage *image = [UIImage imageWithData:data];
    25.    
    26.    // 4.刷新imageView(主进程)
    27.    [self performSelector:@selector(reloadImageV:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
    28. }
    29. - (void)reloadImageV:(UIImage *)image
    30. {
    31.    self.imageView.image = image;
    32. }

    3> Xcode 7 需要设置允许加载http请求,修改info.plist文件,添加红色标记的键值对


    4> 计算代码块执行时间

    第一种方法

        NSDate *start = [NSDate date];

        //2.根据url地址下载图片数据到本地(二进制数据)

        NSData *data = [NSData dataWithContentsOfURL:url];


        NSDate *end = [NSDate date];

        NSLog(@"第二步操作花费的时间为%f",[end timeIntervalSinceDate:start]);


    第二种方法

        CFTimeInterval start = CFAbsoluteTimeGetCurrent();

        NSData *data = [NSData dataWithContentsOfURL:url];


        CFTimeInterval end = CFAbsoluteTimeGetCurrent();

        NSLog(@"第二步操作花费的时间为%f",end - start);

    4.GCD

    1> 基本知识

    队列和任务

    同步函数和异步函数


    2> 基本使用

    01 异步函数+:开启一条线程,串行执行任务

    03 同步函数+并发队并发队列:开启多条线程,并发执行任务

    02 异步函数+串行队列列:不开线程,串行执行任务

    04 同步函数+串行队列:不开线程,串行执行任务

    05 异步函数+主队列:不开线程,在主线程中串行执行任务

    06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)

    07 注意同步函数和异步函数在执行顺序上面的差异


    3> 具体代码实现

    • 1.GCD 异步函数并行队列(系统提供了一个获取全局并行队列)

    1. - (void)asyncConcurrent
    2. {
    3.    // 异步函数并行队列: 会开启多条线程, 无序执行
    4.    
    5.    // 1.并行队列
    6.    /*
    7.     DISPATCH_QUEUE_CONCURRENT : 并行队列
    8.     DISPATCH_QUEUE_SERIAL : 串行队列
    9.     第一参数: 队列的标识名, 主要用于程序奔溃时,根据奔溃log中的标识名快速定位到队列中的哪个代码块导致
    10.     第二个参数: 队列的属性, 是串行队列还是并行队列
    11.     */
    12. //    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT);
    13.    
    14.    // 并行全局队列
    15.    /*
    16.     #define DISPATCH_QUEUE_PRIORITY_HIGH 2
    17.     #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
    18.     #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
    19.     #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
    20.     第一个参数: 队列优先级
    21.     */
    22.    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    23.    
    24.    // 2.异步并行队列
    25.    for (int i = 0; i < 50; ++i) {
    26.        
    27.        dispatch_async(queue, ^{
    28.            
    29.            NSLog(@"%d - %@", i, [NSThread currentThread]);
    30.            
    31.        });
    32.    }
    33. }
    • 2.GCD 异步函数串行队列
    1. - (void)asyncSerial
    2. {
    3.    // 异步函数串行队列: 只会创建一条线程, 有序执行
    4.    // 1.串行队列
    5.    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_SERIAL);
    6.    
    7.    // 2.异步函数
    8.    dispatch_async(queue, ^{
    9.        
    10.        for (int i = 0; i < 10; ++i) {
    11.            NSLog(@"%d - %@", i, [NSThread currentThread]);
    12.        }
    13.        
    14.    });
    15. }
    • 3.GCD 同步并行队列

    1. - (void)syncConcurrent
    2. {
    3.    // 同步并行队列 : 不会创建线程且在当前中执行
    4.    // 1.创建并行队列
    5.    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT);
    6.    
    7.    // 2.创建同步函数
    8.    dispatch_sync(queue, ^{
    9.        
    10.        for (int i = 0; i < 10; ++i) {
    11.            NSLog(@"%i - %@", i, [NSThread currentThread]);
    12.        }
    13.        
    14.    });
    15. }
    • 4.GCD 同步串行队列

    1. // 4.GCD 同步串行队列
    2. - (void)syncSerial
    3. {
    4.    // 同步并行队列 : 不会创建线程且在当前中执行
    5.    // 1.创建串行队列
    6.    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.com", DISPATCH_QUEUE_SERIAL);
    7.    
    8.    
    9.    // 2.创建同步函数
    10.    dispatch_sync(queue, ^{
    11.        
    12.        for (int i = 0; i < 10; ++i) {
    13.            NSLog(@"%@", [NSThread currentThread]);
    14.        }
    15.        
    16.    });
    17. }
    • 5.GCD 异步函数主队列

    1. // 5.GCD 异步函数主队列
    2. - (void)asyncMain
    3. {
    4.    // 异步函数主队列 : 串行执行
    5.    // 1.获取主队列
    6.    dispatch_queue_t queue = dispatch_get_main_queue();
    7.    
    8.    // 2.创建异步函数
    9.    dispatch_async(queue, ^{
    10.        
    11.        for (int i = 0; i < 10; ++i) {
    12.            NSLog(@"%i - %@", i, [NSThread currentThread]);
    13.        }
    14.        
    15.    });
    16. }
    • 6.GCD 同步函数主队列

    1. // 6.GCD 同步函数主队列
    2. - (void)syncMain
    3. {
    4.    // 同步函数主队列 : 会照成死锁现象
    5.    // 原因 : 同步函数是串行执行,且必须执行,队列是主函数,而要执行的任务也在主函数,这样就会造成死锁现象
    6.    // 1.获取主队列
    7.    dispatch_queue_t queue = dispatch_get_main_queue();
    8.    
    9.    NSLog(@"----");
    10.    
    11.    // 2.创建同步函数
    12.    dispatch_sync(queue, ^{
    13.        for (int i = 0; i < 10; ++i) {
    14.            NSLog(@"%i - %@", i, [NSThread currentThread]);
    15.        }
    16.    });
    17. }


    4> GCD线程间的通信

    1. // 点击按钮
    2. - (IBAction)btnClick {
    3.    
    4.    
    5.    // 创建异步函数并发队列
    6.    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    7.        
    8.        // 获取url路径
    9.        NSURL *url = [NSURL URLWithString:@"http://www.chinanews.com/cr/2014/0108/1576296051.jpg"];
    10.        
    11.        // 加载二进制
    12.        NSData *data = [NSData dataWithContentsOfURL:url];
    13.        
    14.        // data转成image
    15.        UIImage *image = [UIImage imageWithData:data];
    16.        
    17.        // 异步函数主函数 刷新UI
    18.        dispatch_async(dispatch_get_main_queue(), ^{
    19.            self.imageView.image = image;
    20.        });
    21.        
    22.    });
    23.    
    24. }


    5> GCD其他函数的使用

    • 1.延迟执行

    1. // 1.延迟执行
    2. - (void)delay
    3. {
    4.    NSLog(@"------start-------");
    5.    
    6.    // 方法1 : NSObject 方法
    7. //    [self performSelector:@selector(run) withObject:self afterDelay:2.0];
    8.    
    9.    // 方法2 : 定时器
    10. //    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    11.    
    12.    // 方法3 : GDC延迟执行
    13.    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    14.        [self run];
    15.    });
    16. }
    • 2.栅栏函数 : 栅栏函数不能用全局并行队列,否则栅栏函数不起作用

    1. // 2.栅栏函数 : 栅栏函数不能用全局并行队列,否则栅栏函数不起作用
    2. - (void)barrier
    3. {
    4.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    5.        for (int i = 0; i < 10; ++i) {
    6.            NSLog(@"01----%@", [NSThread currentThread]);
    7.        }
    8.    });
    9.    
    10.    
    11.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    12.        for (int i = 0; i < 10; ++i) {
    13.            NSLog(@"02----%@", [NSThread currentThread]);
    14.        }
    15.    });
    16.    
    17.    
    18.    // 栅栏函数,保证上面的函数执行完毕后再执行栅栏函数后面的函数(不能用全局并行队列)
    19.    dispatch_barrier_async(dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT), ^{
    20.        NSLog(@"**********************************");
    21.    });
    22.    
    23.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    24.        for (int i = 0; i < 10; ++i) {
    25.            NSLog(@"03----%@", [NSThread currentThread]);
    26.        }
    27.    });
    28.    
    29.    
    30.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    31.        for (int i = 0; i < 10; ++i) {
    32.            NSLog(@"04----%@", [NSThread currentThread]);
    33.        }
    34.    });
    35. }
    • 3.一次性函数

    1. // 3.一次性函数
    2. - (void)once
    3. {
    4.    NSLog(@"xxxx");
    5.    
    6.    static dispatch_once_t onceToken;
    7.    dispatch_once(&onceToken, ^{
    8.        // 此代码块只会执行一次
    9.        NSLog(@"once");
    10.    });
    11. }
    • 4.迭代函数

    1. // 4.迭代函数
    2. - (void)iterative
    3. {
    4.    // for循环迭代 : 有序执行
    5.    for (int i = 0; i < 10; ++i) {
    6.        NSLog(@"for = %i - %@", i, [NSThread currentThread]);
    7.    }
    8.    
    9.    // GCD 迭代 : 无序执行
    10.    /*
    11.     第一参数: 迭代次数
    12.     第二个参数: 队列类型
    13.     第三个参数: 索引
    14.     */
    15.    dispatch_apply(10, dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
    16.        NSLog(@"apply = %zd - %@", index, [NSThread currentThread]);
    17.    });
    18. }
    • 5.迭代的应用(剪切文件)

    1. // 5.迭代的应用(剪切文件)
    2. - (void)iterativeTest
    3. {
    4.    // 1.创建文件管理器
    5.    NSFileManager *mgr = [NSFileManager defaultManager];
    6.    
    7.    // 2.源和目标文件夹路径
    8.    NSString *fromPath = @"/Users/admin/Desktop/from";
    9.    NSString *toPath = @"/Users/admin/Desktop/to";
    10.    
    11.    // 3.获取源文件夹下的所有文件
    12.    NSArray *fileArray = [mgr subpathsAtPath:fromPath];
    13.    NSLog(@"%@", fileArray);
    14.    
    15.    // 4.for循环拼接文件路径剪切文件
    16.    for (NSString *fileName in fileArray) {
    17.        // 拼接源文件路径
    18.        NSString *fromFile = [fromPath stringByAppendingPathComponent:fileName];
    19.        // 拼接目的文件路径
    20.        NSString *toFile = [toPath stringByAppendingPathComponent:fileName];
    21.        
    22.        [mgr moveItemAtPath:fromFile toPath:toFile error:nil];
    23.    }
    24.    
    25.    // 5.GCD 迭代剪切文件
    26.    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    27.        dispatch_apply(fileArray.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
    28.            
    29.            // 拼接源文件路径
    30.            NSString *fromFile = [fromPath stringByAppendingPathComponent:fileArray[index]];
    31.            // 拼接目的文件路径
    32.            NSString *toFile = [toPath stringByAppendingPathComponent:fileArray[index]];
    33.            
    34.            [mgr moveItemAtPath:toFile toPath:fromFile error:nil];
    35.            
    36.        });
    37.    });
    38. }


    6> 各种队列的执行效果


    并发队列

    手动创建的串行队列

    主队列

    同步(sync)

    没有开启新线程

    串行执行任务

    没有开启新线程

    串行执行任务

    没有开启新线程

    串行执行任务

    异步(async)

    有开启新线程

    并发执行任务

    有开启新线程

    串行执行任务

    没有开启新线程

    串行执行任务

    注意

    p使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列


    7> 队列组

    • 创建队列

    1. // 1.创建队列
    2. dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT);
    • 队列组的创建

    1. // 2.创建组
    2. dispatch_group_t group = dispatch_group_create();
    • 创建异步函数下载图片1

    1. dispatch_group_async(group, queue, ^{
    2.       // 3.1 加载URl
    3.        NSURL *url = [NSURL URLWithString:@"http://tb2.bdstatic.com/tb/static-puser/widget/celebrity/img/single_member_100_0b51e9e.png"];
    4.        // 3.2 加载二进制
    5.        NSData *data = [NSData dataWithContentsOfURL:url];
    6.        
    7.        // 3.3 二进制转为图片
    8.        self.image1 = [UIImage imageWithData:data];
    9.        
    10.    });
    • 创建异步函数下载图片2

    1. // 4.创建异步函数下载图片2
    2.    dispatch_group_async(group, queue, ^{
    3.        // 3.1 加载URl
    4.        NSURL *url = [NSURL URLWithString:@"http://tb2.bdstatic.com/tb/static-puser/widget/celebrity/img/single_member_100_0b51e9e.png"];
    5.        // 3.2 加载二进制
    6.        NSData *data = [NSData dataWithContentsOfURL:url];
    7.        
    8.        // 3.3 二进制转为图片
    9.        self.image2 = [UIImage imageWithData:data];
    10.        
    11.    });
    • 合并图片 (dispatch_group_notify 方法会等待group组中的任务执行完之后再执行此线程中的任务)

    1. // 5.合并图片 (dispatch_group_notify 方法会等待group组中的任务执行完之后再执行此线程中的任务)
    2. dispatch_group_notify(group, queue, ^{
    3. // 5.1 创建上下文
    4. UIGraphicsBeginImageContext(CGSizeMake(200, 400));
    5. // 5.2 绘制图片1
    6. [self.image1 drawInRect:CGRectMake(0, 0, 200, 200)];
    7. // 5.3 绘制图片2
    8. [self.image2 drawInRect:CGRectMake(0, 200, 200, 200)];
    9. // 5.4 从上下文种获取图片
    10. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    11. // 5.5 关闭上下文,销毁图片1和图片2
    12. UIGraphicsEndImageContext();
    13. self.image1 = nil;
    14. self.image2 = nil;
    15. // 5.6 刷新图片
    16. dispatch_async(dispatch_get_main_queue(), ^{
    17. self.imageView.image = image;
    18. });
    19. });

    5. NSOperation

        NSOperation并不具备创建线程的能力,但它的两个子类可以创建线程

        子类:

        NSInvocationOperation

        NSBlockOperation

        队列:

        NSOpreationQueue


    1. NSInvocationOperation的创建,在主线程中执行任务需要手动启动

    1. NSInvocationOperation *p1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(running) object:nil];
    2. [p1 start];
    2. NSBlockOperation
    1. // 2.NSBlockOperation : 直接创建的任务在主线程中执行, 追加的任务在子线程中执行, 需手动开启start
    2. NSBlockOperation *p2 = [NSBlockOperation blockOperationWithBlock:^{
    3. NSLog(@"+++++++++%@", [NSThread currentThread]);
    4. }];
    5. // 追加任务
    6. [p2 addExecutionBlock:^{
    7. NSLog(@"1++++++++++%@", [NSThread currentThread]);
    8. }];
    9. [p2 start];

    3. 操作和队列的应用

    . 创建队列

    . 创建操作

    . 将操作添加到队列中

    1. // 创建队列
    2. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    3. // 创建操作
    4. NSBlockOperation *p1 = [NSBlockOperation blockOperationWithBlock:^{
    5. for (int i = 0; i < 10000; ++i) {
    6. NSLog(@"4----%d-----%@", i, [NSThread currentThread]);
    7. }
    8. }];
    9. // 将操作添加到队列中
    10. // 向队列中添加任务
    11. [queue addOperation:p1];

    4. 队列分为主队列和非主队列

    • 主队列中的操作都在主线中执行

    1. NSOperationQueue *queue = [NSOperationQueue mainQueue];
    • 非主队列,通过alloc创建,具备并发和串行创建子线程能力,默认是并发,

    1. NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
    • 设置并发数,设置为1为串行

    1. // 设置并发数, 如果值设置为1即为串行执行
    2. queue.maxConcurrentOperationCount = 2;
    • 队列暂停,YES为暂停,正在执行的任务无法暂停,只能暂停未执行的操作

    1. self.queue.suspended = YES;
    • 队列取消,正在执行的任务无法取消,只能取消未执行的操作(取消之后无法再重新执行)

    1. // 取消队列 任务
    2. [self.queue cancelAllOperations];

    5. 自定义NSOpreation

    创建一个类继承于NSOpreation,用这个类创建NSOpreation对象,添加到队列中,将任务封装在NSOpreation的main对象函数中,就可以在子线程中执行

    1. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    2. {
    3. LDOperation *q = [[LDOperation alloc] init];
    4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    5. [queue addOperation:q];
    6. }
    1. #import "LDOperation.h"
    2. @implementation LDOperation
    3. - (void)main
    4. {
    5. NSLog(@"---------main--------%@", [NSThread currentThread]);
    6. }
    7. @end


    6. 设置任务的依赖关系

    创建多个操作p1/p2/p3/p4, 可设定p2操作在p4操作完成之后再执行

    1. // 设置依赖关系
    2. [p2 addDependency:p4];

    7. NSOpreadtion线程间的通信

    1. - (void)downloadOneImage
    2. {
    3. // 创建队列
    4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    5. // 封装任务
    6. [queue addOperationWithBlock:^{
    7. // 获取图片URL
    8. NSURL *url = [NSURL URLWithString:@"http://img4.duitang.com/uploads/blog/201310/18/20131018212924_ZXZLs.thumb.700_0.jpeg"];
    9. // 加载二进制
    10. NSData *data = [NSData dataWithContentsOfURL:url];
    11. // 将二进制转为图片
    12. UIImage *image = [UIImage imageWithData:data];
    13. // 刷新图片
    14. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    15. NSLog(@"%@", [NSThread currentThread]);
    16. self.imageView.image = image;
    17. }];
    18. }];
    19. }

    8. 在NSOpreation创建的线程中合并图片

    1. - (void)downloadTwoImage1
    2. {
    3. // 创建队列
    4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    5. // 封装任务
    6. [queue addOperationWithBlock:^{
    7. /* ------------获取图片1---------------- */
    8. // 获取图片1URL
    9. NSURL *url = [NSURL URLWithString:@"http://if.topit.me/f/53/3f/1104263230b173f53fl.jpg"];
    10. // 加载二进制
    11. NSData *data = [NSData dataWithContentsOfURL:url];
    12. // 将二进制转为图片
    13. UIImage *image1 = [UIImage imageWithData:data];
    14. /* ------------获取图片2---------------- */
    15. // 获取图片2
    16. url = [NSURL URLWithString:@"http://imgsrc.baidu.com/forum/w%3D580/sign=2daebc56d8b44aed594ebeec831d876a/59ee3d6d55fbb2fbe8a0e70e4d4a20a44623dc27.jpg"];
    17. data = [NSData dataWithContentsOfURL:url];
    18. UIImage *image2 = [UIImage imageWithData:data];
    19. /* ------------合并图片---------------- */
    20. // 开启上下文
    21. UIGraphicsBeginImageContext(CGSizeMake(300, 150));
    22. // 绘制图片1
    23. [image1 drawInRect:CGRectMake(0, 0, 150, 150)];
    24. // 绘制图片2
    25. [image2 drawInRect:CGRectMake(150, 0, 150, 150)];
    26. // 获取上下文中图片
    27. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    28. UIGraphicsEndImageContext();
    29. // 刷新图片
    30. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    31. NSLog(@"%@", [NSThread currentThread]);
    32. self.imageView.image = image;
    33. }];
    34. }];
    35. }

    通过依赖关系

    1. - (void)downloadTwoImage2
    2. {
    3. // 创建队列
    4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    5. NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    6. // 封装任务
    7. NSBlockOperation *p1 = [NSBlockOperation blockOperationWithBlock:^{
    8. /* ------------获取图片1---------------- */
    9. // 获取图片1URL
    10. NSURL *url = [NSURL URLWithString:@"http://if.topit.me/f/53/3f/1104263230b173f53fl.jpg"];
    11. // 加载二进制
    12. NSData *data = [NSData dataWithContentsOfURL:url];
    13. // 将二进制转为图片
    14. UIImage *image1 = [UIImage imageWithData:data];
    15. self.image1 = image1;
    16. }];
    17. NSBlockOperation *p2 = [NSBlockOperation blockOperationWithBlock:^{
    18. /* ------------获取图片2---------------- */
    19. // 获取图片2
    20. NSURL *url = [NSURL URLWithString:@"http://imgsrc.baidu.com/forum/w%3D580/sign=2daebc56d8b44aed594ebeec831d876a/59ee3d6d55fbb2fbe8a0e70e4d4a20a44623dc27.jpg"];
    21. NSData *data = [NSData dataWithContentsOfURL:url];
    22. UIImage *image2 = [UIImage imageWithData:data];
    23. self.image2 = image2;
    24. }];
    25. NSBlockOperation *p3 = [NSBlockOperation blockOperationWithBlock:^{
    26. /* ------------合并图片---------------- */
    27. // 开启上下文
    28. UIGraphicsBeginImageContext(CGSizeMake(300, 150));
    29. // 绘制图片1
    30. [self.image1 drawInRect:CGRectMake(0, 0, 150, 150)];
    31. // 绘制图片2
    32. [self.image2 drawInRect:CGRectMake(150, 0, 150, 150)];
    33. // 获取上下文中图片
    34. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    35. UIGraphicsEndImageContext();
    36. self.image1 = nil;
    37. self.image2 = nil;
    38. self.imageView.image = image;
    39. }];
    40. // 设置执行顺序
    41. [p3 addDependency:p1];
    42. [p3 addDependency:p2];
    43. // 任务添加到队列
    44. [queue addOperation:p1];
    45. [queue addOperation:p2];
    46. [mainQueue addOperation:p3];
    47. }



  • 相关阅读:
    8天学通MongoDB——第三天 细说高级操作
    8天学通MongoDB——第二天 细说增删查改
    8天学通MongoDB——第一天 基础入门
    Redis Web界面管理工具
    Redis 起步
    使用 Swagger UI 与 Swashbuckle 创建 RESTful Web API 帮助文件
    面试应该如何面?
    Login oracle for external authenticate
    突然发现我脾气变好了
    一种持续构建构想
  • 原文地址:https://www.cnblogs.com/Xfsrn/p/5000308.html
Copyright © 2020-2023  润新知