zoukankan      html  css  js  c++  java
  • 玩转iOS开发

    前言

    本文主要介绍iOS多线程开发中使用的主要技术:NSOperation, GCD。 NSThread, pthread。 内容依照开发中的优先推荐使用的顺序进行介绍,涉及多线程底层知识比較多的NSThread, pthread 放到了后面。建议小伙伴们先看文件夹。依据自己的需求来阅读。

    NSOperation

    简单介绍

    使用NSOperation和NSOperationQueue能简单高效的实现多线程编程

    NSOperation和NSOperationQueue实现多线程的详细步骤:

    (1)先将须要运行的操作封装到一个NSOperation对象中
    (2)然后将NSOperation对象加入到NSOperationQueue中
    (3)系统会⾃动将NSOperationQueue中的NSOperation取出来
    (4)将取出的NSOperation封装的操作放到⼀条新线程中执⾏

    NSOperation的子类

    NSOperation是个抽象类,并不具备封装操作的能力,必须使⽤它的子类

    (1)不能直接使用(方法没有实现)
    (2)约束子类都具有共同的属性和方法

    使用NSOperation⼦类的方式有3种:

    (1)NSInvocationOperation
    (2)NSBlockOperation
    (3)自己定义子类继承NSOperation,实现内部对应的⽅法

    NSInvocationOperation

    创建NSInvocationOperation 对象

        - (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;

    调用start 方法開始运行操作

    - (void)start;

    一旦运行操作,就会调用target 的 selector 方法

    注意:

    默认情况下,调用start方法后并不会开一条新线程去运行操作,而是当前线程同步运行操作;仅仅有将NSOperation 加入到NSOperationQuene中。才会异步运行操作;

    1. 运行操作

    //创建操作
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector: @selector(downloadFile:) object:@"fileName"]; //在当前线
    
    //程运行方法(開始运行操作)
    [op start]; 

    2. 把操作加入到队列(并開始异步运行)

    //创建操作 
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector: @selector(downloadFile:) object:@"fileName"]; 
    
    //将操作加入到队列,会自己主动异步调用方法 
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:op]; 
    
    - (void)downloadFile:(id)object
    {
        NSLog(@"下载:%@----线程:%@",object,[NSThread currentThread]); 
    } 

    3. 开启多个线程,不会顺序运行

    我们要记住:

    NSOperation是对GCD的封装。而GCD并发队列,异步运行。

    //队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    for (int i = 0; i < 10; i++) 
    { 
        //创建操作 
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector: @selector(downloadFile:) object:@(i)]; 
    
        //将操作加入到队列,会自己主动异步调用方法 
        [queue addOperation:op]; 
    } 
    
    - (void)downloadFile:(id)object
    {
        NSLog(@"下载:%@----线程:%@",object,[NSThread currentThread]); 
    }

    注意:

    默认情况下。假设操作没有放到队列中queue中,都是同步运行。仅仅有将NSOperation放到一个NSOperationQueue中,才会异步运行操作

    加入操作到NSOperationQueue中

    - (void)addNOperation:(NSOperation *)op;
    - (void)addOperationWithBlock:(void(^)(void))block

    NSBlockOperation

    创建NSBlockOperation对象

    + (id)blockOperationWithBlock:(void)^(void)

    通过addExecutionBlock 方法加入很多其它的操作

    - (id)addExecutionBlock:(void)(^)(void)block

    注意:仅仅要在NSBlockOperation封装的操作数>1, 就会一步运行操作;

    1. NSBlockOperation

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    for (int i = 0; i < 10; i++)
    { 
            //创建操作
            NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ 
                NSLog(@"down %d %@",i,[NSThread currentThread]); 
            }];         
    
            //把操作加入到队列中
            [queue addOperation:op]; 
    } 

    2. NSOperationQueue加入block的operation

    以下我们直接把操作的block代码块加到队列中,是不是代码更简洁啦,block是不是用起来非常爽-_-

    NSOperationQueue *queue = [[NSOperationQueue alloc] init]; 
    
    for (int i = 0; i < 10; i++) 
    {
        [queue addOperationWithBlock:^{ 
            NSLog(@"down %d %@",i,[NSThread currentThread]);
        }]; 
    } 

    3. 全局操作队列

    以下我们定义一个全局队列来调度全部的异步操作

    @property (nonatomic, strong) NSOperationQueue *queue; 
    
    //懒载入队列
    - (NSOperationQueue *)queue
    { 
        if (_queue == nil) 
        { 
            _queue = [[NSOperationQueue alloc] init]; 
        } 
    
        return _queue;
    }
    
    for (int i = 0; i < 10; i++)
    {
        [self.queue addOperationWithBlock:^{ 
            NSLog(@"down %d %@",i,[NSThread currentThread]); 
        }]; 
    } 

    4. 监听操作完毕

    [op1 setCompletionBlock:^{ 
        NSLog(@"....."); 
    }]; 

    并发数

    • 并发数:同一时候执⾏行的任务数.比方,同一时候开3个线程运行3个任务,并发数就是3
    • 最大并发数:同一时间最多仅仅能运行的任务的个数。

    最⼤大并发数的相关⽅方法

    - (NSInteger)maxConcurrentOperationCount;
    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

    注意:

    • 假设没有设置最大并发数。那么并发的个数是由系统内存和CPU决定的,可能内存多就会开多一点。内存少就开少一点。

    • num的值并不代表线程的个数,仅仅代表线程的ID。

    • 最大并发数不要乱写(5以内)。不要开太多。一般以2~3为宜,由于尽管任务是在子线程进行处理的,可是cpu处理这些过多的子线程可能会影响UI,让UI变卡。

    - (void)demo
    {
        self.queue.maxConcurrentOperationCount = 3;
    
        for (int i=1; i<50; i++)
        {
            [self.queue addOperationWithBlock:^{
                [NSThread sleepForTimeInterval:3.0];
                NSLog(@"====%@===%d",[NSThread currentThread],i);
        }];
    
        [NSThread sleepForTimeInterval:1.0];
        }
    }

    不加最大并发数:

    • 此时会创建非常多线程,线程数越多说明线程池更大了。

      可是线程越多越耗资源,分配线程的时间也就越多。所以使用线程的时候要合适最好

    • GCD通常仅仅会开启5~6个线程

    • 通过设置sleepForTimeInterval能够延迟线程运行时间,也能够降低线程数。可是一来于操作的运行时间。

    加入最大并发数:

    • 把操作加入到队列
    [ self.queue addOperationWithBlock]
    • 去线程池去取空暇的线程,假设没有就创建线程

    • 把操作交给从线程池中取出的线程运行

    • 运行完毕后,把线程再放回线程池中

    • 反复2,3,4直到全部的操作都运行完

    队列的取消,暂停和恢复

    取消队列的全部操作

    - (void)cancelAllOperations;

    提⽰:

    也能够调用NSOperation的 - (void)cancel;⽅法取消单个操作

    暂停和恢复队列

    - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
    
    - (BOOL)isSuspended; //当前状态

    暂停和恢复的适用场合

    在tableview界面,开线程下载远程的网络界面,对UI会有影响。使用户体验变差。那么这样的情况。就能够设置在用户操作UI(如滚动屏幕)的时候。暂停队列(不是取消队列),停止滚动的时候,恢复队列。

    演示样例代码-摇奖机

    这里写图片描写叙述

    #import "ViewController.h"
    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UILabel *lbl0;
    @property (weak, nonatomic) IBOutlet UILabel *lbl1;
    @property (weak, nonatomic) IBOutlet UILabel *lbl2;
    
    @property (weak, nonatomic) IBOutlet UIButton *btn;
    //全局队列
    @property (nonatomic, strong) NSOperationQueue *queue;
    
    - (IBAction)start:(UIButton *)sender;
    @end
    
    @implementation ViewController
    
    //队列懒载入-> 假设没有初始话queue, 则不会有队列生成,程序不会有响应
    - (NSOperationQueue *)queue
    {
        if (_queue == nil) 
        {
            _queue = [[NSOperationQueue alloc] init];
        }
        return _queue;
    }
    
    - (void)viewDidLoad 
    {
        [super viewDidLoad];   
    }
    
    - (IBAction)start:(UIButton *)sender 
    {
        //首先要推断队列是否为空->推断队列是否是挂起状态时,并不会推断队列中是否有操作
        if(self.queue.operationCount == 0)
        {
            //假设操作为空,说明运行摇奖,同一时候開始button变暂停
            [self.queue addOperationWithBlock:^{
                [self random];
            }];
            [self.btn setTitle:@"停止" forState:UIControlStateNormal];
            //当前队列运行。不被挂起
            self.queue.suspended = NO;
    
        }
        else if(self.queue.isSuspended)
        {
            //假设当前是挂起状态->開始队列->btn:暂停
            self.queue.suspended = NO;
            [self.btn setTitle:@"停止" forState:UIControlStateNormal];
        }
        else
        {
            //假设当前是运行状态->暂停队列->btn:開始
            self.queue.suspended = YES;
            [self.btn setTitle:@"開始" forState:UIControlStateNormal];
        }
    
    }
    
    - (void)random 
    {
        //假设单签队列没有挂起
        while (![NSOperationQueue currentQueue].isSuspended) 
        {
            int num = arc4random_uniform(10);
            int num1 = arc4random_uniform(10);
            int num2 = arc4random_uniform(10);
    
            [NSThread sleepForTimeInterval:0.05];
    
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                self.lbl0.text = [NSString stringWithFormat:@"%d",num];
                self.lbl1.text = [NSString stringWithFormat:@"%d",num1];
                self.lbl2.text = [NSString stringWithFormat:@"%d",num2];
            }];
        }
    }
    @end

    操作优先级

    设置NSOperation在queue中的优先级,能够改变操作的执⾏优先级

    - (NSOperationQueuePriority)queuePriority;
    - (void)setQueuePriority:(NSOperationQueuePriority)p;

    优先级的取值

    • NSOperationQueuePriorityVeryLow = -8L,

    • NSOperationQueuePriorityLow = -4L,

    • NSOperationQueuePriorityNormal = 0,

    • NSOperationQueuePriorityHigh = 4,

    • NSOperationQueuePriorityVeryHigh = 8

    说明:优先级高的任务,调用的几率会更大。

    操作依赖

    NSOperation之间能够设置依赖来保证运行顺序。⽐如一定要让操作A运行完后,才干运行操作B:

    [opB addDependency:opA];

    能够在不同queue的NSOperation之间创建依赖关系

    注意:

    不能循环依赖(不能A依赖于B,B又依赖于A)

    1. 模拟软件的部分升级

    /*=======依赖关系========*/
    /**
    *模拟软件的部分升级:下载->解压->通知用户升级
    */
    //下载压缩包-> 操作
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"下载 %@",[NSThread currentThread]); 
    }];
    
    //解压,拷贝到对应文件夹
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"解压 %@",[NSThread currentThread]); 
    }];
    
    //通知用户
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"通知用户升级完毕 %@",[NSThread currentThread]); 
    }];
    
    //设置操作的依赖关系
    [op2 addDependency:op1];
    [op3 addDependency:op2];
    
    //加入操作
    /**
    *waitUntilFinished YES 等待全部的操作运行完毕 会堵塞窗口的运行
    *waitUntilFinished NO 不等待
    */
    [self.queue addOperations:@[op1,op2,op3] waitUntilFinished:NO];
    NSLog(@"over");

    循环依赖

    发生循环依赖,程序不会死锁。界面也不会堵塞,操作不会运行
    [op2 addDependency:op1];
    [op3 addDependency:op2];
    [op1 addDependency:op3];

    3. 依赖关系能够跨队列运行

    [op2 addDependency:op1];
    [op3 addDependency:op2];
    
     //子队列
     [self.queue addOperations:@[op1,op2] waitUntilFinished:NO];
    NSLog(@"over");
    
    [[NSOperationQueue mainQueue] addOperation:op3];

    提示

    任务加入的顺序并不能够决定运行顺序,运行的顺序取决于依赖。

    使用Operation的目的就是为了让开发者不再关心线程。

    操作的监听

    能够监听一个操作的运行完毕:

    - (void (^)(void))completionBlock;
    - (void)setCompletionBlock:(void (^)(void))block;
    - (void)demo
    {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            for (int i=1; i<3; i++)
            {
                [NSThread sleepForTimeInterval:1.0];
                NSLog(@"%@==download image = %d",[NSThread currentThread], i);
            }
        }];
    
        [self.queue addOperation:op];
    
        op.completionBlock =^{
            NSLog(@"finishing download image, do ot
    her things");
        };
    
        NSLog(@"%@====I'm later than downloading image",[NSThread currentThread]);
    }

    注意:

    • 下载图片的操作在子线程中运行,op仅仅是加入到队列里了。至于什么时候运行须要等待对应的线程;

    • 因此第三个输出出如今最前面。且在主线程中。

    • completionBlock中的操作要等待下载图片完毕。

    GCD和NSOperation的比較

    GCD

    • GCD是iOS4.0推出的 ,主要针对多核cpu做了优化,是C语言的技术
    • GCD是将任务(block)加入到队列(串行/并行/全局/主队列),而且以同步/异步的方式运行任务的函数

    GCD提供了一些NSOperation不具备的功能

    • 一次性运行
    • 延迟运行
    • 调度组

    NSOperation

    • NSOperation是iOS2.0推出的,iOS4之后重写了NSOperation
    • NSOperation将操作(异步的任务)加入到队列(并发队列),就会运行指定操作的函数

    NSOperation里提供的方便的操作 :

    • 最大并发数
    • 队列的暂停/继续
    • 取消全部的操作
    • 指定操作之间的依赖关系(GCD能够用同步实现)

    困了,待续,最近更新……

    GCD

    NSThread

    Pthread

  • 相关阅读:
    轉載:Case具有两种格式。简单Case函数和Case搜索函数。
    一個可以蓋住 DropDownList 的 div
    UpdatePanel屬性的超級組合
    C#发送Email邮件三种方法的总结(转载)
    使用 Using 語句釋放有限的資源
    轉載:利用List的Sort()、Find()、FindAll()、Exist()來解決一些問題
    Div 的 絕對定位與相對定位
    一個非技術的問題(我的電腦中毒了,電腦桌面多了一個IE的快捷方式,無論如何也刪除不了)
    轉:SQL Server中Case 的不同用法
    一個簡單的排班方法
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5342232.html
Copyright © 2011-2022 走看看