ios - iphone nsoperation 应用程序卡住

标签 ios iphone nsoperation nsoperationqueue

我做了一个 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 在执行多项操作时会卡住,但在一次下载时不会卡住。但是,您的代码示例引发了一些想法:

  1. 并发操作:

    而不是在 main 中有一个 while 循环,你通常会定义你的操作是并发的(即返回 YES是并发的)。然后 movieCompletelyDownloadedWithUrl 将发布 isFinished 事件,这将触发操作的完成。

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

    @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,这将确保 isExecutingisFinished 通知被发布。您还想响应取消事件,确保在操作取消时取消下载。

    参见 Configuring Operations for Concurrent Execution 并发编程指南部分了解更多详细信息。

  2. 直到 main 才开始下载:

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

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

  4. Downloader 如何调用 moveCompleteDownloadedWithUrl

    顺便说一句,您没有展示Downloader 可能如何调用moveCompleteDownloadedWithUrl。这看起来很可疑,但我只是希望您在发布代码时能简化代码。但是,如果您没有使用协议(protocol)委托(delegate)模式或完成 block 模式,那么我会非常担心您的多个 Downloader 对象如何通知相应的 MyDownloadOperation 对象下载完成。就个人而言,我可能倾向于将这两个不同的类重构为一个类,但这是个人品味的问题。

关于ios - iphone nsoperation 应用程序卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20438378/

相关文章:

iphone - UILocalNotification 触发日期问题

iphone - Cocos2d - 我可以将多个 Sprite 帧加载到帧缓存中吗

iphone - 我可以在 iOS 项目名称中添加表情符号图标吗?

ios - 如何取消 NSOperation 的依赖?

ios - 为什么这个 UITapGestureRecognizer 无法识别点击?

ios - 当我使用 StoreKit 的 requestReview 功能时,有什么方法可以知道用户是否点击了提交或不现在或取消按钮?

ios - Xcode:如何制作 launchScreen Storyboard

iphone - 从 nsdata 在 UIScrollView 上添加图像数量

swift - 为什么NSOperation触发此崩溃?

objective-c - 使用 NSOperation 出奇地慢