• 自定义NSOperation


    如果NSInvocationOperation和NSBlockOperation对象不能满足需求, 我们可以直接继承NSOperation, 并添加额外的功能。

    继承所需的工作量主要取决于你要实现非并发还是并发的NSOperation。定义同步的NSOperation要简单许多,只需要重载-main这个方法,在这个方法里面执行主任务,并正确地响应取消事件; 对于异步NSOperation, 必须重写NSOperation的多个基本方法进行实现(main、start)。

    1. 自定义非并发NSOperation

    1. 重写NSOperation的main方法,该方法会作为NSOperation所启动线程的执行体
      • 在 operation 的 main 方法里面,必须提供 autorelease pool,因为你的 operation 完成后需要销毁。
    2. 实现KVO机制,一旦你的 operation 开始了,必须通过 KVO,告诉所有的监听者,现在该operation的执行状态。(即修改isFinished,isExecuting的值)
    3. 重载 isExecuting方法和isFinished方法。在这两个方法中,必须返回一个线程安全值(通常是个BOOL值),这个值可以在 operation 中进行操作。

    1.1 示例代码:

    .h文件:

    typedef void(^HYBDownloadReponse)(UIImage *image);
    
    @interface DownloadOperation : NSOperation
    
    @property (nonatomic, copy) NSString *url;
    @property (nonatomic, copy) HYBDownloadReponse responseBlock;
    
    - (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion;
    
    @end	
    

    .m文件:

    #import "DownloadOperation.h"
    
    @interface DownloadOperation () {
    BOOL _isFinished;
    BOOL _isExecuting;
    }
    
    @end
    
    @implementation DownloadOperation
    
    - (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion {
    if (self = [super init]) {
        self.url = url;
        self.responseBlock = completion;
    }
    
    return self;
    }
    
    // 必须重写这个主方法
    - (void)main {
    // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
    @autoreleasepool {
        // 提供一个变量标识,来表示我们需要执行的操作是否完成了,当然,没开始执行之前,为NO
        BOOL taskIsFinished = NO;
        UIImage *image = nil;
        
        // while 保证:只有当没有执行完成和没有被取消,才执行我们自定义的相应操作
        while (taskIsFinished == NO && [self isCancelled] == NO){
            // 获取图片数据
            NSURL *url = [NSURL URLWithString:self.url];
            NSData *imageData = [NSData dataWithContentsOfURL:url];
            image = [UIImage imageWithData:imageData];
            NSLog(@"Current Thread = %@", [NSThread currentThread]);
            
            // 这里,设置我们相应的操作都已经完成,后面就是要通知KVO我们的操作完成了。
            
            taskIsFinished = YES;
        }
        
        // KVO 生成通知,告诉其他线程,该operation 执行完了
        [self willChangeValueForKey:@"isFinished"];
        [self willChangeValueForKey:@"isExecuting"];
        _isFinished = YES;
        _isExecuting = NO;
        [self didChangeValueForKey:@"isFinished"];
        [self didChangeValueForKey:@"isExecuting"];
        
        if (self.responseBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.responseBlock(image);
            });
        }
    }
    }
    
    - (void)start {
    // 如果我们取消了在开始之前,我们就立即返回并生成所需的KVO通知
    if ([self isCancelled]){
        // 我们取消了该 operation,那么就要告诉KVO,该operation已经执行完成(isFinished)
        // 这样,调用的队列(或者线程)会继续执行。
        [self willChangeValueForKey:@"isFinished"];
        _isFinished = NO;
        [self didChangeValueForKey:@"isFinished"];
    } else {
        // 没有取消,那就要告诉KVO,该队列开始执行了(isExecuting)!那么,就会调用main方法,进行同步执行。
        [self willChangeValueForKey:@"isExecuting"];
        _isExecuting = YES;
        [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
        [self didChangeValueForKey:@"isExecuting"];
    }
    }
    
    - (BOOL)isExecuting {
    return _isExecuting;
    }
    
    - (BOOL)isFinished {
    return _isFinished;
    }
    
    @end	
    

    1.2调用

    queue方式:

    DownloadOperation *operation = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
    //    self.imageView.image = image;
    }];
    
    DownloadOperation *operation1 = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
    //    self.imageView1.image = image;
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperation:operation];
    [queue addOperation:operation1];
    [queue setMaxConcurrentOperationCount:2];
    

    手动方式:

    DownloadOperation *operation = [[DownloadOperation alloc] initWithUrl:@"https://mmbiz.qlogo.cn/mmbiz/sia5QxFVcFD0wkCgnmf6DVxI6fVewNS8rhtZb71v2DMpDy8jIdtviaetzicwQzTEoKKyHAN96Beibk2G61tZpezQ0Q/0?wx_fmt=png" completion:^(UIImage *image) {
    //    self.imageView.image = image;
    }];
    [operation start];		
    

    2. 自定义并发NSOperation

    1. 和同步一样需要 重写mian start
    2. 和同步一样需要 使用KVO通知状态改变
    3. 和同步一样需要 重写isExecuting isFinishing
    4. 比同步多的是 重写isAsynchronous(或者isConcurrent)

    2.1 示例代码:

    #import "DownloadOperation.h"
    
    @interface DownloadOperation () {
    @private BOOL _isFinished;
    @private BOOL _isExecuting;
    }
    
    @end
    
    @implementation DownloadOperation
    
    - (instancetype)initWithUrl:(NSString *)url completion:(HYBDownloadReponse)completion {
    if (self = [super init]) {
        self.url = url;
        self.responseBlock = completion;
    }
    
    return self;
    }
    
    // 必须重写这个主方法
    - (void)main {
    // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池
    @autoreleasepool {
        UIImage *image = nil;
        if (!self.isCancelled) {
            // 获取图片数据
            NSURL *url = [NSURL URLWithString:self.url];
            NSData *imageData = [NSData dataWithContentsOfURL:url];
            image = [UIImage imageWithData:imageData];
        }
        
        NSLog(@"currentThread: %@", [NSThread currentThread]);
        
        // 被取消,也可能发生在转换的地方
        if (self.isCancelled) {
            image = nil;
        }
        
        
        if (![self isCancelled] && self.responseBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                self.responseBlock(image);
            });
        }
    }
    }
    
    // 与自定义同步NSOperation不同的是,必须要实现下面的方法
    #if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_7_0
    - (BOOL)isConcurrent {
    return YES;
    }
    
    #else
    
    - (BOOL)isAsynchronous {
    return YES;
    }
    #endif
    
    @end
  • 相关阅读:
    哦!Python爬虫的这4种方法优缺点你知道吗?
    当你认为Python程序慢时!几个方法你使用了吗!
    Python数据可视化你了解多少!看一下这些是你了解的吗 ?
    王者英雄你喜欢那个!利用Python网络爬虫教你抓取喜欢的英雄图片!
    使用Python时多少有人走过的坑!你是否也遇到过呢!
    Python的68个内置函数你真正的了解多少!建议合理的运用起来!
    使用Python 进行字串格式化的几种方式!你平时是否使用过呢!
    在找工作吗?今天教你使用Python网络爬虫获取招聘信息!来体验一下
    Azure上A/D系列虚拟机到DS系列迁移(1)
    超大文件上传到Azure Linux虚拟机最佳实践
  • 原文地址:https://www.cnblogs.com/sunyanyan/p/5407448.html
Copyright © 2020-2023  润新知