自定義NSOperation進行多線程之間的通信(通知、代理、block)

每一個應用程序至少有一個主線程。線程的工作就是去執行一系列的指令。在Cocoa Touch中,主線程包含應用程序的主運行迴路。幾乎所有你寫的代碼都會在主線程中執行,除非你特別創建了一個單獨的線程,並在這個新線程中執行代碼。

線程有兩個顯著的特徵:

1.每個線程都有訪問你的應用程序資源的同等權限;它包括訪問除了局部變量之外的所有的對象。所以,任何對象都可能被任意線程修改,使用並且改變。

2.沒有辦法可以去預測一個線程會運行多久或者哪個線程會首先完成!


關於自定義NSOperation 類,可以遵循以下步驟:

1.繼承NSOperation

2.重寫“main”方法

3.“main”方法中創建一個“autoreleasepool”

4.將你的代碼放在“autoreleasepool”

創建你自己的自動釋放池的原因是,你不能訪問主線程的自動釋放池,所以你應該自己創建一個。


一、使用通知進行線程之間的通信


1)自定義 NSOperation 文件中;



@interface ZTDownloadOperation : NSOperation

@property(nonatomic ,strong)UIImage *image;

@property(nonatomic ,copy)NSString *urlString;

@end

@implementation ZTDownloadOperation

-(void)main
{
    // 爲了防止子線程中的對象不能及時釋放,手動添加 autoreleasepool.
    @autoreleasepool {
        
        // 下載圖片.
        self.image = [self downloadWebImageWithUrlString:self.urlString];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            // 需求: 利用通知將 下載操作 傳遞出去.
            [[NSNotificationCenter defaultCenter] postNotificationName:@"downloadOperation" object:self];
        });
        
    }
}


// 下載圖片
- (UIImage *)downloadWebImageWithUrlString:(NSString *)urlString
{
    NSLog(@"downloadWebImageWithUrlString%@",[NSThread currentThread]);
    
    NSURL *url = [NSURL URLWithString:urlString];
    
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    UIImage *image = [UIImage imageWithData:data];
    
    return image;
}

2)主控制器中;


@interface ViewController ()

@property(nonatomic ,strong) NSOperationQueue *queue;

//storyboard中拖拽一個imageView
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

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

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //添加通知的觀察者
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setUpImageWithNotify:) name:@"downloadOperation" object:nil];
    
}


-(void)setUpImageWithNotify:(NSNotification *)notify
{
    SHDownloadOperation *op = notify.object;
    
    self.imageView.image = op.image;
}

-(void)dealloc
{
    // 移除通知觀察者.
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"touchesBegan");
    
    // 1. 創建自定義操作.
    SHDownloadOperation *op = [[SHDownloadOperation alloc] init];
    
    // 告訴操作下載哪一張圖片.
    op.urlString = @"http://...";
    
    // 2. 執行操作
    [self.queue addOperation:op];
    
}

3 通知使用注意:

    通知效率是最低的.通知是最靈活最簡單.兩個對象沒有聯繫,利用通知來傳值.

    使用通知的時候,一定要注意移除通知觀察者.

    通知是同步的,注意線程.



二、使用代理進行線程之間的通信

主體框架與上述代碼基本相似,需要修改的是:

 1 在自定義NSOperation .h文件中,添加代理協議及代理屬性
@protocol SHDownloadOperationDelegate <NSObject>

@optional

-(void)downloadWebImageWithImage:(UIImage *)downloadImage;

@end

 2 在自定義NSOperation .m文件中,修改回到主線程函數中的代碼

// 實現代理
if ([self.delegate respondsToSelector:@selector(downloadWebImageWithImage:)]) {
    
    [self.delegate downloadWebImageWithImage:self.image];
}

 3 在主控制器中設置代理,並實現代理方法

// 實現代理方法
-(void)downloadWebImageWithImage:(UIImage *)downloadImage
{
    NSLog(@"代理方法:%@",[NSThread currentThread]);
    
    self.imageView.image = downloadImage;
}


三、使用block進行線程之間的通信

 1 在自定義NSOperation .h文件中

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

// 1.定義block 類型
typedef void(^downloadBlock)(UIImage *image);

@interface SHDownloadOperation : NSOperation

// 下載好的圖片.
@property(nonatomic ,strong)UIImage *image;

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

// 定義block屬性
@property(nonatomic ,copy) downloadBlock block;

@end

 2 在自定義NSOperation .m文件中, 修改回到主線程函數中的代碼

// 3. 執行block
if (self.block) {
    NSLog(@"2.執行block中的內容.");
    self.block(self.image);
}

 3 在主控制器中

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{   
    // 1. 創建自定義操作.
    SHDownloadOperation *op = [[SHDownloadOperation alloc] init];
    
    // 設置圖片url地址
    op.urlString = @"http://...";
 
    // 2. 定義 block 中執行的內容.
    // block 參數 image :就是下載好的圖片.
    op.block = ^(UIImage *image){
        
        self.imageView.image = image;
        NSLog(@"3.最後執行這塊代碼.");
    };
    
    // 2. 執行操作
    [self.queue addOperation:op];
    
}


總的來說,通知的效率最低,一般當兩個對象沒有聯繫時,利用通知進行傳值;block使用起來最爲方便,所以block在工作中使用最爲廣泛.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章