关于NSOperation的基本知识,点击查看我之前转发的博客: 猛戳这里
NSOperation实现并发有两种方式:
①自定义NSOperation只需实现main方法,然后加入到NSOperationQueue
②自定义NSOperation实现start,isExecuting,isFinished,isConnurrent,,然后【operation start】
简单说下我自己的理解:NSOperation能够实现并发是因为start的时候需要分配了一个线程,如果加入到queue中,这些基本的配置queue全包了,只需要重载main方法实现任务即可;如果是手动实现就需要重载start,分配线程,同时需要广播operation的状态,所以需要重载isFinished等。
看到这里基本很明显了,NSOperation只能使用上述的其中一种,这样operation的状态就不会混乱。
如果我想实现NSOperation既能加入到queue又能手动start呢,就像ASI?
一、实现一个手动start的并发NSOperation
#import <Foundation/Foundation.h>
@interface ImageDownloadOperation :NSOperation
@end
在.m中添加以下几个属性,用来发布NSOperation的状态
#import "ImageDownloadOperation.h"
@interface ImageDownloadOperation ()
@property(nonatomic ,assign , getter = isFinished)BOOL IOFinished;
@property(nonatomic ,assign , getter = isExecuting)BOOL IOExecuting;
@property(nonatomic ,assign , getter = isConcurrent)BOOL IOConcurrent;
@end
@implementation ImageDownloadOperation
-(void)setIOConcurrent:(BOOL)IOConcurrent{
_IOConcurrent = IOConcurrent;
}
//发布NSOperation的状态
- (void)setIOFinished:(BOOL)IOFinished{
[selfwillChangeValueForKey:@"isFinished"];
_IOFinished = IOFinished;
[selfdidChangeValueForKey:@"isFinished"];
}
//发布NSOperation的状态
- (void)setIOExecuting:(BOOL)IOExecuting{
[selfwillChangeValueForKey:@"isExecuting"];
_IOExecuting = IOExecuting;
[selfdidChangeValueForKey:@"isExecuting"];
}
@end
这里直接设置IOFinished的getter为父类的isFinished,就省略了在重载isFinished的方法。重载IOFinished的set方法,告知外界NSOperation的状态,这是重中之重,尤其是在加入到queue的这种做法,会影响到queue释放NSOperation。当广播isFinished,NSOperation.completionBlock就会调用。
②配置一些基本的请求属性
通过类名就能看出这个NSOperation是用来下图片的,所以需要配置一些网络请求相关的属性;修改.m中的interface如下:
#import "ImageDownloadOperation.h"
@interface ImageDownloadOperation ()
@property(nonatomic ,assign , getter = isFinished)BOOL IOFinished;
@property(nonatomic ,assign , getter = isExecuting)BOOL IOExecuting;
@property(nonatomic ,assign , getter = isConcurrent)BOOL IOConcurrent;
@property(nonatomic ,strong) NSURLConnection* connection;
@property(nonatomic ,strong) void(^completion)(NSData* data);
@property(nonatomic ,strong) NSURLRequest* request;
@property(nonatomic ,strong) NSMutableData* buff;
@end
添加了一个connection,request,回调completion,和一个data容器。现在再添加一个init方法,初始化这些属性,这里直接给出实现:
-(instancetype)initWithRequest:(NSURLRequest*)request compeletion:(void(^)(NSData* data))completion{
self = [superinit];
if (self) {
self.request = request;
self.buff = [NSMutableDatadata];
self.completion = completion;
_IOConcurrent =YES;
}
return self;
}
这里说明下IOConcurrent,如果说只需要NSOperation实现并发,只需要直接重载isConcurrent返回yes就可以了,不过博客开头,我说了要做到能同步,所以这个属性要写成活的。
③重载start方法
#pragma mark - 重载方法
- (void)start{
self.connection = [[NSURLConnectionalloc] initWithRequest:self.requestdelegate:selfstartImmediately:NO];
[self.connectionstart];
}
首先初始化好connection,并且添加代理@interface ImageDownloadOperation ()<NSURLConnectionDataDelegate>
#pragma mark - NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
}
@property(nonatomic ,strong) NSThread* thread;
#pragma mark - 重载方法
- (void)start{
self.thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(main)object:nil];
[self.threadstart];
}
- (void)main{
self.connection = [[NSURLConnectionalloc] initWithRequest:self.requestdelegate:selfstartImmediately:NO];
[self.connectionstart];
}
#pragma mark - NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
if (self.completion) {
self.completion(nil);
}
self.IOExecuting =NO;
self.IOFinished =YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.buffappendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (self.completion) {
self.completion(self.buff);
}
self.IOExecuting =NO;
self.IOFinished =YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
}
#pragma mark - 重载方法
- (void)start{
self.IOFinished =NO;
self.IOExecuting =YES;
self.thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(main)object:nil];
[self.threadstart];
}
- (IBAction)IOClick:(id)sender {
NSLog(@"IO click");
self.imageVIew.image =nil;
NSURLRequest* request = [NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://f.hiphotos.baidu.com/image/w%3D1920%3Bcrop%3D0%2C0%2C1920%2C1200/sign=8db50237259759ee4a5064c280cb7875/50da81cb39dbb6fdd729831c0a24ab18972b379d.jpg"]cachePolicy:NSURLRequestReloadIgnoringCacheDatatimeoutInterval:60.f];
self.operationA = [[ImageDownloadOperationalloc] initWithRequest:request
compeletion:^(NSData *data) {
NSLog(@"IO completion");
if (data) {
UIImage* img = [UIImageimageWithData:data];
[[NSOperationQueuemainQueue] addOperationWithBlock:^{
[self.imageVIewsetImage:img];
}];
}
}];
[self.operationAsetCompletionBlock:^{
NSLog(@"IO real completionBlock");
}];
[self.operationAstart];
NSLog(@"2....");
}
- (void)main{
NSLog(@"ID main");
self.connection = [[NSURLConnectionalloc] initWithRequest:self.requestdelegate:selfstartImmediately:NO];
[self.connectionstart];
[[NSRunLoopcurrentRunLoop] run];
}
效果图:二、实现一个手动start的同步NSOperation
- (void)start{
NSLog(@"ID start in isMain : %@",[NSThread isMainThread]?@"yes":@"no");
self.IOFinished = NO;
self.IOExecuting = YES;
if (self.isConcurrent) {
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(main) object:nil];
[self.thread start];
}else{
self.thread = [NSThread currentThread];
[self main];
}
NSLog(@"IO start thread = %@ isMain = %@",self.thread,self.thread.isExecuting?@"yes":@"no");
}
这里用IOConcurrent的的getter也是需要重载的父类方法来做判断,如果是同步,就直接调用main,也可以使用perform[self performSelector:@selector(main) onThread:self.thread withObject:nil waitUntilDone:NO];
-(void)startSync;
在.m中添加实现
-(void)startSync{
self.IOConcurrent = NO;
[self start];
}
③开辟了线程测试下:
- (IBAction)IOClick:(id)sender {
NSLog(@"IO click");
self.imageVIew.image = nil;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"queue thread = %@",[NSThread currentThread]);
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/w%3D1920%3Bcrop%3D0%2C0%2C1920%2C1200/sign=8db50237259759ee4a5064c280cb7875/50da81cb39dbb6fdd729831c0a24ab18972b379d.jpg"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.f];
self.operationA = [[ImageDownloadOperation alloc] initWithRequest:request
compeletion:^(NSData *data) {
NSLog(@"IO completion");
if (data) {
UIImage* img = [UIImage imageWithData:data];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.imageVIew setImage:img];
}];
}
}];
[self.operationA setCompletionBlock:^{
NSLog(@"IO real completionBlock");
}];
[self.operationA startSync];
NSLog(@"2....");
[[NSRunLoop currentRunLoop] run];
});
}
手比较懒就随便写了个dispatch,如果真实项目中最好不要这样写,四不像、代码混乱效果图:
:
三、实现NSOperation加入到NSOperationQueue
//发布NSOperation的状态
- (void)setIOFinished:(BOOL)IOFinished{
if (!self.isInQueue) [self willChangeValueForKey:@"isFinished"];
_IOFinished = IOFinished;
if (!self.isInQueue) [self didChangeValueForKey:@"isFinished"];
}
//发布NSOperation的状态
- (void)setIOExecuting:(BOOL)IOExecuting{
if (!self.isInQueue) [self willChangeValueForKey:@"isExecuting"];
_IOExecuting = IOExecuting;
if (!self.isInQueue) [self didChangeValueForKey:@"isExecuting"];
}
- (void)start{
if (self.isInQueue) {
[super start];
}
else{
NSLog(@"ID start in isMain : %@",[NSThread isMainThread]? @"yes":@"no");
self.IOFinished = NO;
self.IOExecuting = YES;
if (self.isConcurrent) {
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(main) object:nil];
[self.thread start];
}else{
self.thread = [NSThread currentThread];
// [self main];
[self performSelector:@selector(main) onThread:self.thread withObject:nil waitUntilDone:NO];
}
// timer = [NSTimer scheduledTimerWithTimeInterval:1. target:self selector:@selector(logo) userInfo:nil repeats:YES];
NSLog(@"IO start thread = %@ isExecuting = %@",self.thread,self.thread.isExecuting?@"yes":@"no");
}
}
#import <objc/runtime.h>
@implementation NSOperationQueue (ImageDownloadOperationQueue)
+(void)load{
Class class = [self class];
SEL orinigalSEL = @selector(addOperation:);
SEL swizzleSEL = @selector(addImageDownloadOperation:);
Method orinigalMD = class_getClassMethod(class, orinigalSEL);
Method swizzleMD = class_getClassMethod(class, swizzleSEL);
BOOL didAddMethod = class_addMethod(class, orinigalSEL, method_getImplementation(swizzleMD), method_getTypeEncoding(swizzleMD));
if (didAddMethod) {
class_replaceMethod(class, swizzleSEL, method_getImplementation(orinigalMD), method_getTypeEncoding(orinigalMD));
}
else{
method_exchangeImplementations(orinigalMD, swizzleMD);
}
}
- (void)addImageDownloadOperation:(NSOperation *)op{
if ([op isKindOfClass:[ImageDownloadOperation class]]) {
__weak ImageDownloadOperation* weakOP = (ImageDownloadOperation*)op;
weakOP.isInQueue = YES;
weakOP.IOConcurrent = YES;
}
[self addImageDownloadOperation:op];
}
@end
- (IBAction)IOClick:(id)sender {
NSLog(@"IO click");
self.imageVIew.image = nil;
NSURLRequest* request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/w%3D1920%3Bcrop%3D0%2C0%2C1920%2C1200/sign=8db50237259759ee4a5064c280cb7875/50da81cb39dbb6fdd729831c0a24ab18972b379d.jpg"] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.f];
ImageDownloadOperation* op = [[ImageDownloadOperation alloc] initWithRequest:request
compeletion:^(NSData *data) {
NSLog(@"IO completion");
if (data) {
UIImage* img = [UIImage imageWithData:data];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.imageVIew setImage:img];
}];
}
}];
[op setCompletionBlock:^{
NSLog(@"IO real completionBlock");
}];
// [self.operationA startSync];
[self.operationQueue addOperation:op];
NSLog(@"2....");
}
四、添加一个startAsync
-(void)startAsync{
self.IOConcurrent = YES;
[self start];
}