我已经创建了一个名为˚的NSOperation
子类来实现多个电影下载。在appDelegate.m
中,我创建了NSOperationQueue
的对象。
- (void)applicationDidFinishLaunching:(UIApplication *)application {
queue = [[NSOperationQueue alloc] init];
[queue setMaximumConcurrentOperationCount:5]
}
MovieDownloadOperation
取决于实际下载电影的名为Downloader
的类
并给出回调movieCompletelyDownloadedWithUrl:
然后,我在downloadState
中创建了一个名为MovieDownloadOperation
的属性。它具有不同的值,例如"STARTED"
,"DOWNLOADING"
,"COMPLETED"
,"ERROR"
。
MyDownloadOperation看起来像
-(id)initWithUrl:(NSURL *)url
{
if (self = [super init])
{
_downloader = [[Downloader alloc] initWithUrl:url];
_downloadState = @"STARTED" ;
}
}
-(void)main
{
while(1)
{
if ([_downloadState isEqualToString:@"COMPLETED"])
{
NSLog(@"movie downloaded successfully");
break ;
}
}
}
-(void)movieCompletelyDownloadedWithUrl:(NSURL *)url
{
_downloadState = @"COMPLETED" ;
}
这适用于一部电影,但当我尝试下载多部电影时,UI会冻结,直到下载第一部电影。我认为问题是while
方法中的main
循环,有没有更好的方法来检查_downloadState
是否更改为"COMPLETED"
??
答案 0 :(得分:2)
目前还不清楚为什么UI会冻结多个操作,但只有一次下载。但是,您的代码示例引发了一些想法:
并行操作:
而不是在while
中使用main
循环,而您通常会将您的操作定义为并发(即从YES
返回isConcurrent
)。然后movieCompletelyDownloadedWithUrl
将发布isFinished
事件,这将触发操作的完成。
就如何进行并发操作而言,您可以定义executing
和finished
的属性:
@property (nonatomic, readwrite, getter = isFinished) BOOL finished;
@property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
您可能希望为网址和下载程序提供strong
属性:
@property (nonatomic, strong) NSURL *url;
@property (nonatomic, strong) Downloader *downloader;
然后您可能在操作子类中包含以下代码:
@synthesize finished = _finished;
@synthesize executing = _executing;
- (id)init
{
self = [super init];
if (self) {
_finished = NO;
_executing = NO;
}
return self;
}
- (id)initWithUrl:(NSURL *)url
{
self = [self init];
if (self) {
// Note, do not start downloader here, but just save URL so that
// when the operation starts, you have access to the URL.
_url = url;
}
return self;
}
- (void)start
{
if ([self isCancelled]) {
self.finished = YES;
return;
}
self.executing = YES;
[self main];
}
- (void)main
{
// start the download here
self.downloader = [[Downloader alloc] initWithUrl:self.url];
}
- (void)completeOperation
{
self.executing = NO;
self.finished = YES;
}
// you haven't shown how this is called, but I'm assuming you'll fix the downloader
// to call this instance method when it's done
- (void)movieCompletelyDownloadedWithUrl:(NSURL *)url
{
[self completeOperation];
}
#pragma mark - NSOperation methods
- (BOOL)isConcurrent
{
return YES;
}
- (void)setExecuting:(BOOL)executing
{
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (void)setFinished:(BOOL)finished
{
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
因此,使用这些方法,您可能会movieCompletelyDownloadedWithUrl
调用completeOperation
,如上所述,这将确保发布isExecuting
和isFinished
通知。您还希望对取消事件做出响应,确保在取消操作时取消下载。
有关详细信息,请参阅并发编程指南的Configuring Operations for Concurrent Execution部分。
请勿在{{1}}之前启动下载:
我没有看到您的main
方法启动下载。这让我感到紧张,你的main
初始化方法Downloader
可能正在启动下载,这很糟糕。您不希望在创建操作时启动下载,而是在操作开始之前不应该执行此操作(例如initWithURL
或start
)。因此,在上面的示例中,我只有main
保存网址,然后initWithURL
开始下载。
在main
中使用NSURLConnectionDataDelegate
方法:
顺便说一下,您没有分享您的操作如何处理网络请求。如果您正在使用NSOperation
方法,当您摆脱NSURLConnectionDataDelegate
中的while
循环时,如果您未在特定时间内安排main
,则可能会遇到问题运行循环。例如,您可以这样做:
NSURLConnection
如果你没有使用NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[connection start];
方法,或者你已经解决了这个运行循环问题,那么请忽略这个忠告,但是,当你在你的NSURLConnectionDataDelegate
方法中修复main
方法时操作,您可能会暴露旧版NSURLConnection
可能隐藏的main
问题。
Downloader
如何调用moveCompleteDownloadedWithUrl
?
顺便说一句,您没有展示Downloader
可能如何调用moveCompleteDownloadedWithUrl
。这看起来很可疑,但我只是希望你在发布时简化你的代码。但是如果你没有使用协议委托模式或完成块模式,那么我会非常担心你的多个Downloader
对象如何通知相应的MyDownloadOperation
对象下载完成。就个人而言,我可能倾向于将这两个不同的类重构为一个,但这是个人品味的问题。
答案 1 :(得分:0)
您可以使用NSTimer
检查下载是否已完成。它不会冻结你的UI
NSTimer *localTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkDownloadStatus) userInfo:nil repeats:YES];
-(void)checkDownloadStatus
{
if ([_downloadState isEqualToString:@"COMPLETED"])
{
NSLog(@"movie downloaded successfully");
[localTimer invalidate];
}
}