[英]iphone nsoperation application freezes
我已經制作了一個名為˚的NSOperation
的子類來實現多個電影下載。 在appDelegate.m
,我創建了一個NSOperationQueue
對象。
- (void)applicationDidFinishLaunching:(UIApplication *)application {
queue = [[NSOperationQueue alloc] init];
[queue setMaximumConcurrentOperationCount:5]
}
MovieDownloadOperation
依賴於一個名為Downloader
的類,它實際上下載了電影並給出了回調movieCompletelyDownloadedWithUrl:
.
然后,我在MovieDownloadOperation
中MovieDownloadOperation
了一個名為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會凍結,直到下載第一部電影。 我認為問題是main
方法中的while
循環,是否有更好的方法來檢查_downloadState
是否更改為"COMPLETED"
?
目前還不清楚為什么UI會凍結多個操作,但不會只有一個下載。 但是,您的代碼示例引發了一些想法:
並發操作:
而不是在main
有一個while
循環,你通常會將你的操作定義為並發(即從isConcurrent
返回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
通知。 您還希望對取消事件做出響應,確保在取消操作時取消下載。
有關更多詳細信息,請參閱“ 並發編程指南”中的 “為並發執行配置操作”部分。
在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
方法的底線,你可能會暴露你的舊的NSURLConnection
問題main
可能已經隱藏了你。
Downloader
如何調用moveCompleteDownloadedWithUrl
?
順便說一下,你沒有展示Downloader
如何調用moveCompleteDownloadedWithUrl
。 這看起來很可疑,但我只是希望你在發布時簡化你的代碼。 但是,如果您沒有使用協議委托模式或完成塊模式,那么我會非常擔心您的多個Downloader
對象如何通知相應的MyDownloadOperation
對象下載完成。 就個人而言,我可能傾向於將這兩個不同的類重構為一個,但這是個人品味的問題。
您可以使用NSTimer
檢查下載是否已完成。 它不會凍結你的用戶界面
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];
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.