iphone nsoperation应用程序冻结

时间:2013-12-07 06:21:11

标签: ios iphone nsoperation nsoperationqueue

我已经创建了一个名为˚的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" ??

2 个答案:

答案 0 :(得分:2)

目前还不清楚为什么UI会冻结多个操作,但只有一次下载。但是,您的代码示例引发了一些想法:

  1. 并行操作:

    而不是在while中使用main循环,而您通常会将您的操作定义为并发(即从YES返回isConcurrent)。然后movieCompletelyDownloadedWithUrl将发布isFinished事件,这将触发操作的完成。

    就如何进行并发操作而言,您可以定义executingfinished的属性:

    @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,如上所述,这将确保发布isExecutingisFinished通知。您还希望对取消事件做出响应,确保在取消操作时取消下载。

    有关详细信息,请参阅并发编程指南Configuring Operations for Concurrent Execution部分。

  2. 请勿在{{1​​}}之前启动下载:

    我没有看到您的main方法启动下载。这让我感到紧张,你的main初始化方法Downloader可能正在启动下载,这很糟糕。您不希望在创建操作时启动下载,而是在操作开始之前不应该执行此操作(例如initWithURLstart)。因此,在上面的示例中,我只有main保存网址,然后initWithURL开始下载。

  3. 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问题。

  4. 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];
     }
 }