# 使用Prefetching API執行異步任務 ###### tags: `Swift` iOS10起,在`UITableView`和`UICollectionView`提供了Prefetching的API 讓你在cellForRow之前就預先開始執行異步任務 也就是說,還沒出現在螢幕上但即將出現的cell 就會被觸發執行 注意: * 一開始未捲動時,螢幕中可見的cell並不會觸發preFetch * 當開始捲動後,觸發preFetch,indexPaths大約包含可視區外7-10個cell的數量 接下來看如何操作 首先你要簽署protocol和設定delegate ``` class ViewController: UITableViewDataSourcePrefetching { tableView.prefetchDataSource = self } ``` `UITableViewDataSourcePrefetching`有兩個功能 ``` // 必要:異步執行下載行為 public func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) // 非必要:取消下載 optional public func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) ``` [官方建議](https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching#2759980)搭配Operation一起實作異步下載的任務 Operation雖比起GCD更耗效能 但可以讓我們取消先前的任務,增加靈活度和複用性 新增兩個參數 ``` let loadingQueue = OperationQueue() var loadingOperations: [IndexPath: DataLoadOperation] = [:] ``` 前者是用來放Operation的queue 後者是一個dict,以IndexPath為key,幫助之後查詢是否正在加載 在`preFetchRowsAt`中這樣操作 ``` func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { for indexPath in indexPaths { // 已在加載,不做事 if let _ = loadingOperations[indexPath] { continue } // 加入queue中並執行 if let loader = dataLoader.load(at: indexPath.row) { loadingQueue.addOperation(dataLoader) loadingOperations[indexPath] = loader } } } ``` 取消任務的話 ``` func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) { for indexPath in indexPaths { if let loader = operations[indexPath] { loader.cancel() operations[indexPath] = nil } } } ``` 接著,你在willDisplayCell裡去檢查檔案是否已載好 注意:preFetch不保證一定可以在cell出現時載好檔案 官方文件有寫道,你必須自己去處理已載好、載入中、未開始的情況 如果載好就可以顯示在cell上 若還沒好,先檢查loadingOperations[indexPath]是否已存在 若還沒生成,就生成它並加入queue以及dict中(與preFetch裡做的事相同) 若已經有了,就等待callback並更新資料 這樣看下來,在程式碼上其實並沒有減少 但多了預載的機制,在格式上看起來更有條理 某些使用情境上的捲動也會較順暢 參考資料: [Butter Smooth Performance of TableView and CollectionView with Prefetching API iOS](https://medium.com/globant/butter-smooth-performance-of-tableview-and-collectionview-with-prefetching-api-ios-428fcc46c56e) [官方文件](https://developer.apple.com/documentation/uikit/uitableviewdatasourceprefetching#2759980) [RayWenderlich](https://www.raywenderlich.com/7341-uicollectionview-tutorial-prefetching-apis)