• (五十六)iOS多线程之NSOperation


    NSOpertation是一套OC的API,是对GCD进行的Cocoa抽象。

    NSOperation有两种不同类型的队列,主队列和自定义队列。

    主队列运行于主线程上,自定义队列在后台运行。


    【NSBlockOperation】

    通过Block创建任务,下面比较主队列和自定义队列的区别:

    将自定义队列声明为成员变量,并进行初始化:

    @property (nonatomic, strong) NSOperationQueue *myqueue;
    self.myqueue = [[NSOperationQueue alloc] init];

    获取主队列的方法为[NSOperationQueue mainQueue]。

    队列有一个方法addOperationWithBlock方法用于添加一个用Block描述的任务。

    具体代码为:

    - (void)NSBlockOperation{
        
        // 自定义队列在子线程中运行。
        [self.myqueue addOperationWithBlock:^{
            NSLog(@"%@",[NSThread currentThread]);
        }];
        // 主队列任务在主线程中运行。
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@"%@",[NSThread currentThread]);
        }];
        
    }
    执行这个方法,得到的结果如下,可见与上面的描述相符。

    2015-02-17 10:46:15.308 NSOpertaion[709:17138] <NSThread: 0x7b9aa440>{number = 2, name = (null)}
    2015-02-17 10:46:15.319 NSOpertaion[709:17067] <NSThread: 0x7b978550>{number = 1, name = main}


    【NSInvocationOperation】

    需要定义一个回调方法,好处是可以接收一个id类型的object作为消息。

    例如:

    - (void)NSInvocationOperation{
        
        NSDictionary *msg = @{@"name" : @"op",@"message" : @"hello"};
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(InvocationCall:) object:msg];
        [self.myqueue addOperation:op];
        
    }

    实现回调方法:

    - (void)InvocationCall:(id)obj{
        NSLog(@"%@ with object %@",[NSThread currentThread],obj);
    }

    调用后,打印的结果如下,可以看到object对象被传了过来。

    2015-02-17 10:56:03.764 NSOpertaion[812:26537] <NSThread: 0x7ba6eb10>{number = 2, name = (null)} with object {
        message = hello;
        name = op;
    }

    【任务执行顺序】

    在默认情况下,自定义队列是并行队列,执行无序;而主队列为串行队列,有序执行。下面进行实验验证说法:

        // 自定义队列在子线程中运行。
        for (int i = 0; i < 9; i++) {
            [self.myqueue addOperationWithBlock:^{
                NSLog(@"%@ with no %d",[NSThread currentThread],i);
            }];
        }
        
        // 主队列任务在主线程中运行。
        for (int i = 0; i < 9; i++) {
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                NSLog(@"%@ with no %d",[NSThread currentThread],i);
            }];
        }

    执行结果如下,可见与上面的描述相同。

    2015-02-17 11:04:08.421 NSOpertaion[919:32337] <NSThread: 0x7bf91f50>{number = 9, name = (null)} with no 6
    2015-02-17 11:04:08.421 NSOpertaion[919:32331] <NSThread: 0x7bf91c10>{number = 4, name = (null)} with no 1
    2015-02-17 11:04:08.421 NSOpertaion[919:32338] <NSThread: 0x7bf915c0>{number = 6, name = (null)} with no 5
    2015-02-17 11:04:08.421 NSOpertaion[919:32336] <NSThread: 0x7dab62c0>{number = 7, name = (null)} with no 4
    2015-02-17 11:04:08.421 NSOpertaion[919:32339] <NSThread: 0x7dab61b0>{number = 3, name = (null)} with no 7
    2015-02-17 11:04:08.421 NSOpertaion[919:32330] <NSThread: 0x7dab6110>{number = 2, name = (null)} with no 0
    2015-02-17 11:04:08.421 NSOpertaion[919:32341] <NSThread: 0x7d97b3f0>{number = 5, name = (null)} with no 8
    2015-02-17 11:04:08.421 NSOpertaion[919:32328] <NSThread: 0x7be6d450>{number = 10, name = (null)} with no 3
    2015-02-17 11:04:08.421 NSOpertaion[919:32329] <NSThread: 0x7dab6450>{number = 8, name = (null)} with no 2
    2015-02-17 11:04:08.448 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 0
    2015-02-17 11:04:08.449 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 1
    2015-02-17 11:04:08.449 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 2
    2015-02-17 11:04:08.449 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 3
    2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 4
    2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 5
    2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 6
    2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 7
    2015-02-17 11:04:08.450 NSOpertaion[919:32281] <NSThread: 0x7d96f040>{number = 1, name = main} with no 8


    【自定义队列顺序执行】

    使用NSBlockOperation对象的addDependency设置依赖关系,只有依赖的对象执行完毕后,自己才能执行。

    例如下面的例子,三个任务要顺序执行,先下载,再处理,最后显示,通过这样的设定可以保证顺序:

    - (void)SerialOperation{
        
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"下载");
        }];
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"处理");
        }];
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"显示");
        }];
        
        [op2 addDependency:op1];
        [op3 addDependency:op2];
        
        [self.myqueue addOperation:op1];
        [self.myqueue addOperation:op2];
        [self.myqueue addOperation:op3];
        
    }
    注意这个执行和串行队列的异步任务不同点是,串行队列的异步任务仅仅开一个线程;自定义队列的顺序执行可能开辟多个但不会太多个线程。

    注意上面的代码有一定的问题,因为显示只有主线程可以处理,所以op3应该放入主线程。

    Tip:依赖关系可以跨队列,因此op3依赖op2在主线程中仍然有效,只需要修改op3的入队代码为:

    [[NSOperationQueue mainQueue] addOperation:op3];

    Tip:注意避开循环依赖,程序会崩溃



    【设定多线程的最大开销】

    设定同时执行的最大线程数:通过队列的setMaxConcurrentOperationCount方法来设定,例如:

    [self.myqueue setMaxConcurrentOperationCount:3];

    应用场景:网络通信,例如3G开3个子线程,WIFI开6个子线程。

    Tip:线程的开销主要是CPU和内存,还会耗电,因此应该考虑软件的能耗。


    Tip:AFNetworing的底层是使用GCD开发的,接口是NSOperation。


  • 相关阅读:
    带你封装自己的『权限管理』框架
    一夜搞懂 | JVM 线程安全与锁优化
    一夜搞懂 | Java 内存模型与线程
    一夜搞懂 | JVM 字节码执行引擎
    一夜搞懂 | JVM 类加载机制
    一夜搞懂 | JVM GC&内存分配
    一文洞悉JVM内存管理机制
    Redis 的基本数据类型 和 基础应用场景
    MyISAM 和 InnoDB 索引结构及其实现原理
    一次性搞懂 PHP 中面向对象的所有知识点。
  • 原文地址:https://www.cnblogs.com/aiwz/p/6154194.html
Copyright © 2020-2023  润新知