【发布时间】:2014-04-28 16:05:30
【问题描述】:
我使用 NSOperation 和 NSOperationQueue 执行下载图像。每个操作都保留一个 StoreThumbRequest 对象,该对象封装了所有请求特定的数据,包括目标视图,等待图像。同时,该视图保留了一个 NSOperation 对象,用于在重用或释放期间取消操作。这个保留循环在适当的时候在“取消”和“主要”方法中被小心地打破。
我发现一个 NSOperation 在设置 maxConcurrentOperationsCount 的限制时会保留在 NSOperationQueue 中。达到此限制会阻止调用新 NSOperations 的“主要”方法。 NSOperation 仅在取消时才保留在队列中。如果它成功地完成了它的任务,它就会被优雅地从它的 NSOperationQueue 中移除。
我的 NSOperations 是非并发的,没有依赖关系。
有什么建议吗?提前感谢
#import "StoreThumbLoadOperation.h"
#import "StoreThumbCache.h"
@interface StoreThumbLoadOperation () <NSURLConnectionDelegate> {
StoreThumbRequest *_request;
NSMutableData *_downloadedData;
NSURLConnection *_connection;
NSPort *_port;
}
@end
@implementation StoreThumbLoadOperation
-(id)initWithRequest: (StoreThumbRequest *)request
{
NSParameterAssert(request);
self = [super init];
if (self) {
_request = request;
}
return self;
}
-(void)main
{
NSURL *url = ...;
NSURLRequest *request = [NSURLRequest requestWithURL: url];
_connection = [[NSURLConnection alloc] initWithRequest: request delegate: self startImmediately: NO];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
_port = [NSMachPort port];
[runLoop addPort: _port forMode: NSDefaultRunLoopMode];
[_connection scheduleInRunLoop: runLoop forMode: NSDefaultRunLoopMode];
[_connection start];
[runLoop run];
}
-(void)cancel
{
[super cancel];
[_connection unscheduleFromRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
[_connection cancel];
_request.thumbView.operation = nil; //break retain loop
[[NSRunLoop currentRunLoop] removePort: _port forMode: NSDefaultRunLoopMode];
}
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
_downloadedData = [NSMutableData new];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_downloadedData appendData: data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if (_connection == connection) {
NSFileManager *fileManager = [NSFileManager new];
NSString *pathForDownloadedThumb = [[StoreThumbCache sharedCache] thumbPathOnRequest: _request];
NSString *pathForContainigDirectory = [pathForDownloadedThumb stringByDeletingLastPathComponent];
if (! [fileManager fileExistsAtPath: pathForContainigDirectory]) {
//create a directory if required
[fileManager createDirectoryAtPath: pathForContainigDirectory withIntermediateDirectories: YES attributes: nil error: nil];
}
if (! self.isCancelled) {
[_downloadedData writeToFile: pathForDownloadedThumb atomically: YES];
UIImage *image = [UIImage imageWithContentsOfFile: pathForDownloadedThumb];
//an image may be empty
if (image) {
if (! self.isCancelled) {
[[StoreThumbCache sharedCache] setThumbImage: image forKey: _request.cacheKey];
if (_request.targetTag == _request.thumbView.tag) {
dispatch_async(dispatch_get_main_queue(), ^{
_request.thumbView.image = image;
});
}
}
}
_request.thumbView.operation = nil; //break retain loop
[[NSRunLoop currentRunLoop] removePort: _port forMode: NSDefaultRunLoopMode];
}
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
if (error.code != NSURLErrorCancelled) {
#ifdef DEBUG
NSLog(@"failed downloading thumb for name: %@ with error %@", _request.thumbName, error.localizedDescription);
#endif
_request.thumbView.operation = nil;
[[NSRunLoop currentRunLoop] removePort: _port forMode: NSDefaultRunLoopMode];
}
}
@end
我了解,NSOperation 不会立即从其 NSOperationQueue 中删除。但它会无限期地保留在那里
来自 NSOperation 取消方法文档
此方法不会强制您的操作代码停止。相反,它会更新对象的内部标志以反映状态的变化。
【问题讨论】:
-
看起来某个操作在开始之前可能会被取消。这可能是问题
标签: ios nsurlconnection nsoperation nsoperationqueue nsrunloop