3.多線程NSOperation

1.NSOperation的基本操做編程

使用NSOperation的兩個子類,NSInvocationOperation 和 NSBlockOperation 建立操做,而後將操做添加到隊列中去執行安全

// NSOperation
    
    // 1. 實例化 NSOperation 子類對象:NSInvocationOperation
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
    
    // 2. 實例化 NSOperation 子類對象
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        //
        NSLog(@"耗時操做2------------");
    }];
    
    
    // NSOperationQueue
    
    // 1.獲取主隊列
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    
    
    // 2.建立非主隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // NSOperation使用: 將操做添加到隊列中!
    [mainQueue addOperation:op];
    
    [queue addOperation:op1];

2.NSOperation定義的操做能夠直接用start啓動,至關於直接執行,入口是NSOperation中定義的main方法網絡

    // 1. 實例化操做對象
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test1) object:nil];
    
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test2) object:nil];

    NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test3) object:nil];
    
    
    // 需求: 1. 三個操做都是耗時操做!
    
//    // 2. 建立非主隊列
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//    
//    // 3. 將操做添加到非主隊列中
//    [queue addOperation:op1];
//    [queue addOperation:op2];
//    [queue addOperation:op3];
    
//    // 需求: 2. 三個操做都是UI操做
//    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
//    // 至關於 GCD 中的 異步函數 + 主隊列!
//    [mainQueue addOperation:op1];
//    [mainQueue addOperation:op2];
//    [mainQueue addOperation:op3];
    
    // NSOperation 執行方式2: 直接啓動!直接在當前線程執行!
    [op1 start];
    [op2 start];
    [op3 start];
    
    // NSOperation 對象的入口是定義在自身內部的 main 方法;
    // 當將操做添加到操做隊列中或者直接調用操做的 start 方法以後,內部都會調用 main 方法,全部二者都可以執行操做!

    
    NSLog(@"touchesEnd");
    
}

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

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


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

3.使用block併發

NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"222222222----%@",[NSThread currentThread]);
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"33333333----%@",[NSThread currentThread]);
    }];
    
    
    // 1.將操做添加到主隊列中
    //    [[NSOperationQueue mainQueue] addOperation:op1];
    //    [[NSOperationQueue mainQueue] addOperation:op2];
    //    [[NSOperationQueue mainQueue] addOperation:op3];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];

4.向blockOperation中追加任務異步

    // 當 NSBlockOperation中的任務數 > 1 以後, 不管是將操做添加到主線程,仍是在主線程直接執行 start ,NSBlockOperation中的任務執行順序都不肯定,執行線程也不肯定!async

    // 一半在開發的時候,要避免向 NSBlockOperation 中追加任務!函數

    // 若是任務都是在子線程中執行,而且不須要保證執行順序,能夠直接追加任務!atom

// 1. 實例化操做對象
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"11111111----%@",[NSThread currentThread]);
    }];
    
    // 往當前操做中追加操做
    [op1 addExecutionBlock:^{
        
        NSLog(@"追加任務1%@",[NSThread currentThread]);
        
    }];
    
    // 往當前操做中追加操做
    [op1 addExecutionBlock:^{
        
        NSLog(@"追加任務2%@",[NSThread currentThread]);
        
    }];
    
    // 當 NSBlockOperation中的任務數 > 1 以後, 不管是將操做添加到主線程,仍是在主線程直接執行 start ,NSBlockOperation中的任務執行順序都不肯定,執行線程也不肯定!
    // 一半在開發的時候,要避免向 NSBlockOperation 中追加任務!
    // 若是任務都是在子線程中執行,而且不須要保證執行順序,能夠直接追加任務!

5.直接經過操做隊列添加任務url

// 直接經過操做隊列添加任務!
    
    //將block中的內容當作一個操做添加到主隊列中!
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        
        NSLog(@"---------1%@",[NSThread currentThread]);
        
    }];
    
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        
        NSLog(@"---------2%@",[NSThread currentThread]);
        
    }];
    
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        
        NSLog(@"---------3%@",[NSThread currentThread]);
        
    }];
    
    
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        
        NSLog(@"---------4%@",[NSThread currentThread]);

    }];
    
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        
        NSLog(@"---------5%@",[NSThread currentThread]);
        
    }];

    
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        
        NSLog(@"---------6%@",[NSThread currentThread]);
        
    }];

6.給操做添加操做依賴,保證操做的順序執行,避免循環依賴spa

// NSOperation 相對於 GCD 來講,增長了如下管理線程的功能:
    // 1. NSOperation能夠添加操做依賴: 保證操做的執行順序!  --> 和GCD中將任務添加到一個串行隊列中是同樣的! 一個串行隊列會對應一條線程!
    
    // GCD 中的按順序執行(串行隊列)----> 串行執行!
    // 添加操做依賴以後,系統有可能串行執行保證任務的執行順序,還有可能採用線程同步技術,保證任務執行順序!
    
    
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil];
    
    
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"-------222 %@",[NSThread currentThread]);
        
    }];
    
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"-------333 %@",[NSThread currentThread]);
        
    }];
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"-------444 %@",[NSThread currentThread]);
        
    }];
    

    // 四個操做都是耗時操做! 而且要求按順序執行! 操做2是UI操做!
    
    // 添加操做依賴的注意點:
    // 1. 不要添加循環依賴!
    // 2. 必定要在將操做添加到操做隊列中以前添加操做依賴!
    
    // 優勢: 對於不一樣操做隊列中的操做,操做依賴依然有效!
    
    // 添加操做依賴!
    [op2 addDependency:op1];
    
    [op3 addDependency:op2];
    
    [op4 addDependency:op3];
    

    
   // [op2 addDependency:op3];
   // [op1 addDependency:op4];
    
    
    // 將操做添加到操做隊列中
    NSOperationQueue *quque = [[NSOperationQueue alloc] init];
    
    [quque addOperation:op3];
    [quque addOperation:op1];
    [quque addOperation:op4];
    
    // 將操做2 添加到主隊列中
    [[NSOperationQueue mainQueue] addOperation:op2];

7.NSOperation的高級操做,暫停/恢復/取消/設置最大線程數 

  // 遇到併發編程,何時選擇 GCD ,何時選擇 NSOperation!

    // 1.簡單的開啓線程/回到主線程,選擇 GCD : 效率更高,簡單!

    // 2.須要管理操做(考慮到與用戶交互!),使用 NSOperation!

 // NSOperation高級操做:
    
    // 1. 添加操做依賴!
    // 2. 管理操做: 重點! 是操做隊列的方法!
    // 2.1 暫停/恢復 取消 操做!
    // 2.2 開啓合適的線程數量!(最多不超過6條!)
    
    // 一半開發的時候,會將操做隊列設置成一個全局的變量(屬性)!
    //
    
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"0----------");
        
        [self test];
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [queue addOperationWithBlock:^{
        
        [self test];
    }];
    
    [queue addOperation:op];
    
    // 1. 暫停操做// 開始滾動的時候
    [queue setSuspended:YES];

    // 2. 恢復操做// 滾動結束的時候
    [queue setSuspended:NO];
    
    
    // 3. 取消全部操做// 接收到內存警告
    [queue cancelAllOperations];
    
    // 3補充: 取消單個操做!是操做的方法!
    [op cancel];
    
    // 設置最大併發數,開啓合適的線程數量 // 實例化操做隊列的時候
    [queue setMaxConcurrentOperationCount:6];
    
    // 遇到併發編程,何時選擇 GCD ,何時選擇 NSOperation!
    // 1.簡單的開啓線程/回到主線程,選擇 GCD : 效率更高,簡單!
    // 2.須要管理操做(考慮到與用戶交互!),使用 NSOperation!

8.block的循環引用問題,使用__weak typeof(self)weakself = self;弱引用

#import "HMViewController.h"

@interface HMViewController ()

// 全局操做隊列
@property(nonatomic ,strong)NSOperationQueue *queue;

//
@property(nonatomic ,strong)NSMutableArray *array;

@end

@implementation HMViewController

-(NSOperationQueue *)queue
{
    if (!_queue) {
        
        _queue = [[NSOperationQueue alloc] init];
        
        [_queue setMaxConcurrentOperationCount:6];
    }
    
    return _queue;
}

-(NSMutableArray *)array
{
    if (!_array) {
        _array = [NSMutableArray array];
    }
    return _array;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor orangeColor];
    //
    NSLog(@"控制器建立成功!");

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"touchesBegan");
    // block!
    
    // 1. 建立操做
    
    // 爲了防止 block 中使用 self 出現的循環引用問題! 通常在 block中使用 self 的時候,要使用 self 的弱引用!!!
    
    // 爲了安全,block中出現self,都使用 弱引用寫法!
    
    // weakself : 下面就是 self 的弱引用寫法!
    __weak typeof(self)weakself = self;
    
    // __weak HMViewController *wself = self;
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        
        [self test];
    }];

    // 將操做添加到 array 中!
    [self.array addObject:op];
    
    // self --> queue -->op --> block --> self  : 循環引用鏈條!
    
    
    // 2. 將操做添加到操做隊列中
    // 由於操做執行完畢以後,操做隊列會自動釋放其中的操做,因此,在操做中(NSblockOperation)block裏有了 self,沒有關係,可以釋放.不會形成循環引用!
    [self.queue addOperation:op];
    
    
}

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

-(void)dealloc
{
    NSLog(@"控制器銷燬了!");
}

9.線程間通訊,在子線程下載圖片,在主線程更新UI

// 1 在子線程下載圖片
    
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        
        // 若是圖片地址中出現了 & 符號,換一張圖片!
        // image :下載好的網絡圖片
        UIImage *image = [self downloadWebImageWithUrlString:@"http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg"];
        
    
        // 回到主線程!
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            
            // 顯示圖片
            self.imageView.image = image;
        }];
    }];
    
    
    [self.queue addOperation:op];
    
    NSLog(@"touchesEnd");
    
}


// 下載網絡圖片的方法
- (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
{
    NSURL *url = [NSURL URLWithString:urlString];
    
    // 下載方法!耗時方法!
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    UIImage *image = [UIImage imageWithData:data];
    
    return image;
}

 10.自定義NSOperation,繼承NSOperation,重寫mian方法

加自動釋放池

//
//  HMDownloadOperation.h
//  09-線程間通訊
//
//  Created by HM on 16/1/25.
//  Copyright © 2016年 HM. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@class HMDownloadOperation;

@protocol HMDownloadOperationDelegate <NSObject>


-(void)downloadImageWithOperation:(HMDownloadOperation *)operation;

@end

@interface HMDownloadOperation : NSOperation

// 代理屬性
@property(nonatomic ,weak) id<HMDownloadOperationDelegate> delegate;

// 寫一個圖片屬性// 下載好的圖片
@property(nonatomic ,strong) UIImage *image;

// 圖片下載地址
@property(nonatomic ,copy)NSString *urlString;



@end
//
//  HMDownloadOperation.m
//  09-線程間通訊
//
//  Created by HM on 16/1/25.
//  Copyright © 2016年 HM. All rights reserved.
//

#import "HMDownloadOperation.h"

@implementation HMDownloadOperation

// 重寫 NSOperation 的 main 方法!
// 當把自定義的操做添加到操做隊列中,或者直接調用 操做的  start 方法以後,都會自動來執行 main 方法中的內容!
-(void)main
{
    // 爲了可以及時釋放內存,通常會手動書寫一個 autoreleasepool!蘋果官方文檔不要求寫!
    @autoreleasepool {
        
        NSLog(@"----------%@",[NSThread currentThread]);
        
        self.image = [self downloadWebImageWithUrlString:self.urlString];
        
        NSLog(@"image:%@",self.image);
        
        // 通知/代理/block :爲了保證在不一樣對象之間傳值的準確性!採用的是同步傳值的方法!
        
        // 回到主線程,執行代理方法:
        dispatch_async(dispatch_get_main_queue(), ^{
            // 執行代理方法
            if ([self.delegate respondsToSelector:@selector(downloadImageWithOperation:)]) {
                [self.delegate downloadImageWithOperation:self];
            }
        });
    }
}


// 下載網絡圖片的方法
- (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
{
    NSURL *url = [NSURL URLWithString:urlString];
    
    // 下載方法!耗時方法!
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    UIImage *image = [UIImage imageWithData:data];
    
    return image;
}

@end

11.操做完成以後的回調completionBlock

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        
        NSLog(@"--------%@",[NSThread currentThread]);
        
    }];
    
    // 操做完成以後的回調!
//    op.completionBlock = ^(){
//     
//        NSLog(@"操做執行完畢!");
//        
//    };
    [op setCompletionBlock:^{
        
        NSLog(@"操做執行完畢!");
    }];
    
    
    [op start];
相關文章
相關標籤/搜索