NSOperationQueue mainQueue 不更新 UICollectionViewCell

Posted

技术标签:

【中文标题】NSOperationQueue mainQueue 不更新 UICollectionViewCell【英文标题】:NSOperationQueue mainQueue do not update UICollectionViewCell 【发布时间】:2015-12-30 04:04:24 【问题描述】:

在使用NSURLSession 下载文件并在UICollectionView 中显示它们时,我发现了一个问题,即单元格无法从NSOperationQueue 块正确更新。下载部分和文件存储每次都能正常工作。但是更新单元格进度或图像仅适用于控制器的初始加载。如果我导航到 root,然后再次导航到我的 Collection 控制器,下载工作但单元格不再更新。我发现这很有趣discussion,但对我来说没有帮助。

更多细节,启动 URL 会话和处理任务的控制器从故事板 segue 加载。我注意到应用程序每次都在创建新的控制器实例。在第二次导航访问单元格不显示进度或更新,但任务/文件已正确下载。似乎我的下载任务正在从其他控制器实例获取单元格的副本。但从我在调试器中看到的情况来看,这没有意义,委托工作正常,任务成功完成。

这是在第二次访问控制器时无法更新单元格的代码方法:

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

    if (totalBytesExpectedToWrite == NSURLSessionTransferSizeUnknown) 
        NSLog(@"Unknown transfer size");
    
    else

    // Locate the FileDownloadInfo object in array based on my unique key in URL.
     int index = [self getFileDownloadInfoIndexWithTaskURL:downloadTask.originalRequest.URL.lastPathComponent];
        // Get singleton download object list from app delegate
        NSMutableArray *globalFileDownloadData = [self getGlobalDownloadFileList];
        FileDownloadInfo *fdi = [globalFileDownloadData objectAtIndex:index];
        // Get the progress view of the appropriate cell and update its progress.
        NSArray *arr = [self.fileDispCollection visibleCells];
        for(FileItemCell* cell in arr)
                if ([fdi.contentId isEqualToString:cell.testLbl.text])

                    [[NSOperationQueue mainQueue] addOperationWithBlock:^

                     // This code works when app start initial visit to controller
                     // but does not work on2nd 3rd or other revisit
                     // even cells found correctly, files also loaded saved
                     // the progress does not show and no update

                        cell.downloadProgress.hidden = NO;
                        cell.downloadProgress.progress = fdi.downloadProgress;
                    ];   
                
            
    

我使用这种方法初始化我的单例会话:

- (NSURLSession *)backgroundSession 
     //disptach_once ensure that multiple background sessions are not 
     //created in this instance of the application. 

    static NSURLSession *session = nil;
    static dispatch_once_t onceToken;

    NSArray *URLs = [[NSFileManager defaultManager]     URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];
    self.docDirectoryURL = [URLs objectAtIndex:0];
    dispatch_once(&onceToken, ^

    NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"com.Transport.demo"];
    sessionConfiguration.HTTPMaximumConnectionsPerHost = 3;
    sessionConfiguration.discretionary = NO;
    session = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                 delegate:self
                                            delegateQueue:nil];
    );
    return session;

什么会导致第二个单元格副本或只是收集单元格在第二轮导航中不响应调用或数据重新加载?希望有人能解决这个问题。

【问题讨论】:

【参考方案1】:

试着把你更新的 UI 代码放在主线程中,如下所示:

dispatch_async(dispatch_get_main_queue(), ^
            //your update UI code
        );

【讨论】:

我确实尝试过,但结果是一样的 dispatch_async(dispatch_get_main_queue(), ^ fdi.dow​​nloadProgress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite; cell.downloadProgress.hidden = NO; cell.downloadProgress.progress = fdi.dow​​nloadProgress; NSLog( @"## Update Cell Progress with ID: %@ content: %@", cell.testLbl.text, fdi.contentId); ); 在我之前的项目中,我和你有同样的问题,有时更新,有时没有,所以我使用 UIView 自定义了一个并更新了框架宽度。【参考方案2】:

答案就在我的问题中。故事板 segue 正在创建新的控制器,它是 URLSession 下载任务的创建者和处理程序。会话是保持初始控制器堆栈和委托的单例。因此新的控制器实例将开始下载,但所有响应都将转到旧控制器实例。

修复很简单,不使用 performSegueWithIdentifier,而是创建单个控制器实例,在这种情况下实际上是更好的解决方案。

if (!self.library)
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle: nil];
        self.library = [storyboard instantiateViewControllerWithIdentifier:@"libraryScene"];
    
    self.library.model = self.selectNode;
    [self.navigationController pushViewController:self.library animated:YES];

【讨论】:

以上是关于NSOperationQueue mainQueue 不更新 UICollectionViewCell的主要内容,如果未能解决你的问题,请参考以下文章

Xcode关闭自动完成问题

didUpdateLocations 总是用不同的坐标多次调用

ios 将一个函数在主线程执行的4种方法

NSOperationQueue 阻塞不相关的 NSOperationQueue?

多线程编程NSOperationQueue

GCD与NSOperationQueue