我做了一个 NSOperation
的子类,叫做 ˚ 来实现多部电影下载。在 appDelegate.m
中,我创建了一个 NSOperationQueue
的对象。
- (void)applicationDidFinishLaunching:(UIApplication *)application {
queue = [[NSOperationQueue alloc] init];
[queue setMaximumConcurrentOperationCount:5]
}
MovieDownloadOperation
依赖于一个名为 Downloader
的类,它实际下载电影
并提供回调 movieCompletelyDownloadedWithUrl:
.
然后,我在 MovieDownloadOperation
中创建了一个名为 downloadState
的属性。它具有不同的值,例如 "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 会卡住,直到下载完第一部电影。我认为问题是 main
方法中的 while
循环,是否有更好的方法来检查 _downloadState
是否更改为 “完成”
??
最佳答案
目前尚不清楚为什么 UI 在执行多项操作时会卡住,但在一次下载时不会卡住。但是,您的代码示例引发了一些想法:
并发操作:
而不是在
main
中有一个while
循环,你通常会定义你的操作是并发的(即返回YES
从是并发的
)。然后movieCompletelyDownloadedWithUrl
将发布isFinished
事件,这将触发操作的完成。就如何进行并发操作而言,您可以定义
executing
和finished
的属性:@property (nonatomic, readwrite, getter = isFinished) BOOL finished; @property (nonatomic, readwrite, getter = isExecuting) BOOL executing;
您可能希望 URL 和下载器具有
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 并发编程指南部分了解更多详细信息。
直到
main
才开始下载:我没有看到您的
main
方法启动下载。这让我感到紧张,因为您的Downloader
初始化方法initWithURL
可能正在启动下载,这很糟糕。您不希望在创建操作时启动下载,而是在操作开始之前不应该这样做(例如start
或main
)。因此,在我上面的示例中,我只有initWithURL
保存 URL,然后main
开始下载。在
NSOperation
中使用NSURLConnectionDataDelegate
方法:顺便说一句,您没有分享您的操作是如何处理网络请求的。如果您正在使用
NSURLConnectionDataDelegate
方法,当您摆脱main
中的while
循环时,如果不安排,您可能会遇到问题特定运行循环中的NSURLConnection
。例如,您可能会这样做:NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; [connection start];
如果你没有使用
NSURLConnectionDataDelegate
方法,或者如果你已经解决了这个运行循环问题,那么忽略这个建议,但是,最重要的是,当你修复main
方法,您可能会暴露旧main
可能对您隐藏的NSURLConnection
问题。Downloader
如何调用moveCompleteDownloadedWithUrl
?顺便说一句,您没有展示
Downloader
可能如何调用moveCompleteDownloadedWithUrl
。这看起来很可疑,但我只是希望您在发布代码时能简化代码。但是,如果您没有使用协议(protocol)委托(delegate)模式或完成 block 模式,那么我会非常担心您的多个Downloader
对象如何通知相应的MyDownloadOperation
对象下载完成。就个人而言,我可能倾向于将这两个不同的类重构为一个类,但这是个人品味的问题。
关于ios - iphone nsoperation 应用程序卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20438378/