最近需要开发一个音乐下载的模块,考虑到方便封装和对象化,使用了NSOpreation
。
考虑到复用,我这里使用了继承NSOpreation
的方法
1 2 3 4 5 6 7 8 9
| @interface YOYMusicDownloadOperation : NSOperation @property (copy, nonatomic) void(^processBlock)(double process); @property (copy, nonatomic) void(^completeBlock)(BOOL r, NSURL *localUrl);
+ (NSString *)localMusicFilesPath;
- (instancetype)initWithURL:(NSURL *)url processCallback:(void(^)(double process))process complete:(void(^)(BOOL finised, NSURL *localUrl))complete;
@end
|
这个类很简单,两个block属性,一个用于进度监控,一个用于结束回调。
然后开始重写NSOpreation
的两个方法:start
和 main
因为是下载需要异步回调,所以在函数main
里使用了死循环,等下载完成后再结束线程。(如果线程结束,这个opreation会被释放)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| - (void)start { if ([self isCancelled]){
[self willChangeValueForKey:@"isFinished"]; finished = YES; [self didChangeValueForKey:@"isFinished"]; return; } else {
[self willChangeValueForKey:@"isExecuting"]; executing = YES; [self main]; [self didChangeValueForKey:@"isExecuting"]; } }
- (void)main { _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[[NSOperationQueue alloc] init]]; NSURLRequest *req = [NSURLRequest requestWithURL:self.downloadUrl]; _task = [_session downloadTaskWithRequest:req]; [_task resume]; while(_task != nil) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } [self willChangeValueForKey:@"isFinished"]; [self willChangeValueForKey:@"isExecuting"]; finished = YES; executing = NO; [self didChangeValueForKey:@"isFinished"]; [self didChangeValueForKey:@"isExecuting"]; } - (BOOL) isFinished{ return(finished); }
- (BOOL) isExecuting{ return(executing); }
|
注意: 在重写NSOpreation
时,注意也要重写 isFinished
和isExecuting
这两个属性的getter,同时还需要手动触发相关的KVO。如果没重写这两个方法,isFinished
和isExecuting
这两个属性会一直是NO,导致在线程完成后还留在NSOpreationQueue里
,不会移除。
然后实现NSURLSession的几个代理方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
_musicLocation = location; } -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
if(!error){ dispatch_sync_on_main_queue(^{ if(self.completeBlock) { if(_musicLocation) { self.completeBlock(YES, _musicLocation); } else { self.completeBlock(NO, nil); } } }); } _task = nil; } -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{ double process = totalBytesWritten/(double)totalBytesExpectedToWrite; if(self.processBlock) { self.processBlock(process); } }
|
特别说明下,因为下载完成后需要刷新UI,我选择了在主线程调用completeBlock