Swift多線程之Operation:異步加載CollectionView圖片
距離上一篇更新又過去了半個月,現在基本上變成每月兩更啦。/(ㄒoㄒ)/~~
其實俺也不想,俺也想學那些勤奮好學的小盆友們,麻利兒的日更。但是臣妾做不到啊,超有難度。就這篇還是在抗爭了無數拖延癥之后,給自己下了死命令,心理想:“今天必須更新,必須更新!!”
就這樣,給自己立下的最后截稿日之后的 三天 ,終于寫完了這個例子。
我們分享了Operation的一些基礎知識,實現了基礎的多線程案例,也會怎么設置優先級了。
今天的開餐小菜是看看怎么設置一個依賴關系的Demo。然后就是一道相對豐盛的大菜,使用Operation在CollectionView上實現子線程加載圖片的案例。這個例子其實在生產中經常可以碰見。先把界面加載出來,然后再慢慢往item里面加載圖片。
這個例子故意寫的稍微復雜了一點,也是為了多學習點新東西嘛。
敲黑板, 敲黑板 , 敲黑板 。知識點有:自定義Operation子類、map函數、Swift特有的元組數據類型。
下面是最終實現的CollectionView異步加載圖片的例子效果:
1. Operation 設置依賴關系
高樓大廈從地起,我們就從今天餐前小點開始。先看看如何設置operation的依賴關系。
啥叫依賴關系?有啥用啊?
打個比方咱們要做一個聽音樂的付費App項目,需要經過登陸、付費、下載、播放四個步驟。其實一看就明白,這四個操作是有先后順序的,但假如所有的操作都是多線程,咱們怎么控制順序?
通過設置“優先級”?NO!優先級沒有辦法干這個事情。要是覺得設置優先級可以實現的,請回去看看俺上一篇文章。 Swift多線程之Operation:按優先級加載圖片
我們可以通過設置依賴關系,建立起先后的順序。只有當一個 operation 所依賴的所有 operation 都執行完成時,這個 operation 才能開始執行。
并且,operation是可以跨隊列建立依賴關系的噢!operation是可以跨隊列建立依賴關系的噢!operation是可以跨隊列建立依賴關系的噢!說了三遍。
需要小小注意的是,要先將operation的依賴關系建立好之后再添加到隊列中。
咱們還是借助上次的那個模板來看看。哎呀哎呀,不要逼我新寫模板了嗎,要講究復用。
其實是懶得寫新的,懶死算了。
看到沒?圖片是按照從上到下依次加載的,不再像之前亂七八糟的順序顯示的了吧。
代碼很簡單,請看:
fileprivate func startDepencyDemo() {
operationQueue.maxConcurrentOperationCount = 4
self.activityIndicator.startAnimating()
guard let url = URL(string: "https://placebeard.it/355/140") else {return }
let op1 = convenienceOperation(setImageView: imageView1, withURL: url)
let op2 = convenienceOperation(setImageView: imageView2, withURL: url)
op2.addDependency(op1)
let op3 = convenienceOperation(setImageView: imageView3, withURL: url)
op3.addDependency(op2)
let op4 = convenienceOperation(setImageView: imageView4, withURL: url)
op4.addDependency(op3)
DispatchQueue.global().async {
[weak self] in
self?.operationQueue.addOperations([op1,op2,op3,op4], waitUntilFinished: true)
DispatchQueue.main.async {
self?.activityIndicator.stopAnimating()
}
}
}
好啦,接下來看看寫那個異步加載CollectionView圖片怎么搞。在這個之前,需要補充點前置知識。
2. 前置知識點內容
2.1 自定義Operation子類
Operation操作狀態.png
- operation狀態是Finished的時候,是沒有辦法取消的。
- operation成功、失敗、或者被取消,isFinished都會被設置為 true 。所以請不要依靠這個屬性來判斷是不是成功執行了。
2.1.1 需要重寫的地方
建立一個Operation的可以并發的子類可能稍微麻煩一點點。默認情況下, operation 的子類是同步執行的,如果要創建一個能夠并發的子類,我們可能需要重寫一些方法。
-
start : 所有并行的 Operations 都必須重寫這個方法,然后在想要執行的線程中手動調用這個方法。注意:任何時候都不能調用父類的start方法。
-
main : 可選的。盡管我們可以在 start 方法中執行任務,但是使用 main 來設置執行任務的代碼,可以讓 operation 的結構更加清晰。
-
isExecuting : 必須的。是否執行中。,需要實現KVO通知機制。
-
isFinished : 必須的。是否已完成。,需要實現KVO通知機制。
-
isAsynchronous :必須的。該方法默認返回 false ,表示非并發執行。并發執行需要自定義并且返回 true 。
2.1.2 代碼實現
fileprivate var _executing : Bool = false
override var isExecuting: Bool {
get { return _executing }
set {
if newValue != _executing {
willChangeValue(forKey: "isExecuting")
_executing = newValue
didChangeValue(forKey: "isExecuting")
}
}
}
fileprivate var _finished : Bool = false
override var isFinished: Bool {
get { return _finished }
set {
if newValue != _finished {
willChangeValue(forKey: "isFinished")
_finished = newValue
didChangeValue(forKey: "isFinished")
}
}
}
override var isAsynchronous: Bool {
get {
return true
}
}
override func start() {
if !isCancelled {
isExecuting = true
isFinished = false
startOperation()
} else {
isFinished = true
}
有童鞋大概很奇怪,為什么要定義 _executing , _finished ?
看到了嗎?只給了get方法,沒有給set方法。所以沒有辦法直接使用這個屬性。
2.1.3 取消操作的說明
operation不是說把屬性 isCancelled 設置一下就好了。其實這個屬性起到的作用只是一個標識,我們在寫代碼的時候需要定期檢查 isCancelled 這個值,如果是 ture ,我們需要立即停止執行接下來的任務。
2.2 map函數
map是干嘛的吶?先舉個栗子,會更容易理解一下下哈。
看看代碼:
let testNumberArray = [1,2,3,4,5,6,7,8,9]
print("沒有使用map之前的打印結果:\(testNumberArray)")
let newArray = testNumberArray.map{$0 + 2}
print("newArray的打印結果:\(newArray)")
let stringArray = testNumberArray.map { (number) -> String in
return "No.\(number.description)"
}
print("stringArray的打印結果:\(stringArray)")
有點懵是不是?沒關系,我們來看看打印結果是什么:
image.png
有沒有很神奇?一個數組,簡簡單單就變成了兩個數組。
Swift是支持一門函數式編程的語言, Map 是針對集合類型的操作。map方法會遍歷調用者,對數組中的每一個元素執行閉包中定義的操作。
咱們 newArray 執行的操作就是把 testNumberArray 數組中每一個元素都加了2。
stringArray 執行的操作就是把 testNumberArray 數組中每一個元素變成字符串,前面加上“No.”
What's the fxxk!厲不厲害?厲不厲害?針對集合的操作還有 FlatMap , Filter , Reduce ,有興趣的童鞋請自行研究哈。
2.3 Swift新增的元組數據類型
元組其實是一個復合值。簡單的而說,就是使用圓括號把多個值組合成一個復合值。元組內的值可以使用任意類型,元組并不要求元組內的值具有相同的類型。
let (day, content) = (30,"今天天氣不錯")
上面這個就是最簡單的一個元組定義。
這是進階一點的。
let week = (name : "星期一" , order : 1)
// 可以很快捷的取出數值
let weekName = week.name
let weekOrder = week.order
元組不是咱們今天的重點,只是在demo里面用到了,就要稍微提一下。
元組可以與Switch大牌進行復雜條件的判斷;可以作為方法的返回值,來返回多個數值;可以假裝成結構體使用;
3. CollectionView中圖片進行異步加載
來看一下思維導圖:
源代碼各位可以自行下載觀看, 只有Swift版本的下載 。
我好像說了句廢話,因為代碼中用了Swift特有的數據格式,當然提供不了Objective-C的源碼了。
給item賦值圖片的重點地方的代碼:
override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let cell = cell as! CollectionCell
let (item, operation) = imageOps[indexPath.row]
// 只是以防萬一,我們先停止一下操作
operation?.cancel()
weak var weakCell = cell
let newOp = ImageLoadOperation(forItem: item) { (image) in
DispatchQueue.main.async {
weakCell?.imageView.image = image
}
}
imageLoadQueu.addOperation(newOp)
imageOps[indexPath.row] = (item, newOp)
}
感謝各位收看,今天的新聞聯播到此結束。
有錢的大爺就點擊下方打賞點賣笑錢,有力氣的就在github上給個星星:sparkles:。或者在評論里面咱們聊聊天,吹吹牛也行。hiahia~
噢,預告一下。按照之前的計劃,下一篇應該是GCD基礎。O~M~G,好枯燥。
來自:http://www.jianshu.com/p/2728ae223a5a