Ios iphone操作应用程序冻结

Ios iphone操作应用程序冻结,ios,iphone,nsoperation,nsoperationqueue,Ios,Iphone,Nsoperation,Nsoperationqueue,我制作了一个名为˚的NSOperation子类,以实现多个电影下载。在appDelegate.m中,我创建了一个NSOperationQueue的对象 - (void)applicationDidFinishLaunching:(UIApplication *)application { queue = [[NSOperationQueue alloc] init]; [queue setMaximumConcurrentOperationCount:5] } moviedown

我制作了一个名为˚的
NSOperation
子类,以实现多个电影下载。在
appDelegate.m
中,我创建了一个
NSOperationQueue
的对象

- (void)applicationDidFinishLaunching:(UIApplication *)application {
   queue = [[NSOperationQueue alloc] init];
   [queue setMaximumConcurrentOperationCount:5]
} 
moviedownload操作
依赖于名为
Downloader
的类,该类实际下载电影 并使用URL:
然后,我在
MovieDownloadOperation
中创建了一个名为
downloadState
的属性。它有不同的值,如
“开始”
“下载”
“完成”
“错误”

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
方法中的
循环,是否有更好的方法来检查
\u downloadState
是否更改为
“COMPLETED”

您可以使用
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];
     }
 }

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

  • 并发操作:

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

    关于如何进行并发操作,您可以定义
    执行
    完成
    的属性:

    @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;
    
    然后在operation子类中可能有以下代码:

    @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"];
    }
    
    因此,使用这些方法,您可以使用URL
    call
    completeOperation
    完全下载
    moviec,如上文所述,这将确保
    正在执行
    已完成
    通知得到发布。您还需要响应取消事件,确保在操作取消时取消下载

    有关更多详细信息,请参阅《并发编程指南》的一节

  • main
    之前不要启动下载:

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

  • 使用
    NSOperation
    中的
    NSURLConnectionDataDelegate
    方法:

    顺便说一句,您没有分享您的操作是如何执行网络请求的。如果您使用的是
    NSURLConnectionDataDelegate
    方法,当您在
    main
    中退出
    循环时,如果您没有在特定的运行循环中安排
    NSURLConnection
    ,则可能会出现问题。例如,您可以执行以下操作:

    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
    [connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
    [connection start];
    
    如果您没有使用
    NSURLConnectionDataDelegate
    方法,或者您已经解决了此运行循环问题,那么请忽略此建议,但底线是,当您在操作中修复
    main
    方法时,您可能会暴露旧的
    main
    可能对您隐藏的
    NSURLConnection
    问题

  • Downloader
    如何调用
    movecompletedownloadewithurl

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