欲知前事如何,且看上回分解: iOS性能優化(初級) git
經過對性能初級優化祕籍一段時間的練習,少俠應該對性能優化有了必定的瞭解,在平常開發編碼中有了些性能優化的意識,當產品小師妹提出一個新的交互的時候,想必也定難不倒少俠了。github
就列表來講,icon、大標題、小標題、內容,通常APP的不少時候就是這幾個元素,排版不一樣,細緻效果不一樣。這些對於少俠來講都已經不是問題了,無聲無息中APP已經如絲般順滑。看着產品小師妹那敬仰的眼神,牛心潮澎湃,花前月下,海誓山盟立刻就要脫口而出。緩存
但,江湖風雲變幻,折戟沉沙你早有準備。性能優化
只是沒想到那一天來的這麼快。bash
那一天產品小師妹提出了一個新的需求,除了以前的icon、大標題、小標題以外,如今要加上標籤,標籤有多個用於各類活動運營,標籤的位置要根據標題內容的位置來定,標籤要作成圓角加邊框,同時列表每一行的高度要根據各項內容來最終肯定,內容多就高,內容少就矮,還有icon要圓形加邊框順便帶點陰影,巴拉巴拉巴拉巴拉巴拉巴拉。
產品小師妹一口氣說了不少,說的你眼冒金星,氣息紊亂,差點走火入魔,口吐鮮血,但看着小師妹那一如既往的欣喜加期待的眼神,只好暗暗運力,穩住陣腳,一口答應小師妹的需求。
伊人遠去,看着小師妹遠去的身影,你瘋狂編碼,但總有那麼一個點沒法突破,流暢性始終沒法達到要求,不由陷入了沉思。異步
初級性能優化祕籍,只能應對初級的性能優化問題。但當前的需求,效果多,子視圖多,排版更新頻繁,高度每行不同。初級祕籍已經不能很好的湊效,這可如何是好。async
少俠莫慌,老夫看你已經熟練了初級性能優化祕籍,基礎已經打牢,如今就將性能優化中級祕籍傳授與你罷。post
工欲善其事必先利其器,想要打敗對手,你要有趁手的兵器。性能
在APP裏直接的觀察看FPS數據:測試
KMCGeigerCounter 也能夠根據 CADisplayLink 本身寫一個簡易好用的,CADisplayLink 是一個定時器,並且這個定時器的調用頻率跟屏幕刷新頻率相同。
頂級法寶,當屬 Instrument:
若想熟練使用此項法寶,需注意兩個地方
打開方式: Xcode -> Product -> Profile -> Core Animation 配合TimeProfile 一塊兒使用 查看FPS的同時,還能查看到哪些操做比較耗時,有此傍身,再厲害的敵人也會露出破綻。
性能優化的步驟:
修改 -> Instrument查看 -> 修改 -> Instrument查看 —> 修改.....
重複以上動做直到性能達到要求
CPU的耗時操做能夠在Instrument裏查看到,並定位修改優化,但GPU的優化要怎麼進行呢?
XCode9以後能夠Xcode -> Debug > View Debugging > Rendering 下看到優化的各個選項,模擬器時沒法勾選,只有真機的狀況下才能勾選。
Color Copied Images — 若是GPU不支持當前圖片格式,那麼圖片會交給CPU進行預先處理,這張圖片會顯示爲藍色。
Color Misaligned Images — 檢測圖片是否被拉伸,當圖片色實際大小跟ImageView的大小不相同時,就會發生,顯示爲黃色,這種操做會比較消耗CPU資源。
Color Offscreen-rendered Yellow — GPU的渲染有兩種,On-screen Rendering當前屏幕渲染,是指GPU的渲染在當前屏幕的緩衝區內進行。off-screen Rendering是指在GPU的渲染髮生在當前屏幕以外新開闢的緩衝區。開闢新的緩衝區,切換緩衝區等會對性能有較大的影響。
觸發離屏渲染有如下幾種行爲:
少俠熟讀了以上招式,便能快速找出對手的破綻。
找出了敵人的破綻,少俠還要制定詳細的應對策略,瞅準時機,方能一招制敵。
老夫這就給你展現制敵之道:
Color Blended Layers:
Color Hits Green and Misses Red: 在初級性能優化中,適當使用shouldRasterize中有詳細講解。
Color Copied Images: 開發過程當中注意圖片格式
Color Misaligned Images: 儘可能把圖片大小設置的和UIImageView相同大小。
Color Offscreen-rendered Yellow: 這是性能優化的要點,針對引發離屏渲染的各類狀況須要逐一應對
重點來了,離屏渲染的優化招式,少俠看仔細了
UIView: 若是view.layer.contents 爲空,直接經過設置view.layer.cornerRadius 以及 view.backgroundColor或者view.layer.border便可設置圓角,不須要設置masksToBounds爲YES,此時不會產生離屏渲染。
//設置圓角邊框
view.layer.cornerRadius = 3.0
view.layer.borderColor = UIColor.red.cgColor
view.layer.borderWidth = 1.0
// 設置帶背景
view.layer.cornerRadius = 3.0
view.backgroundColor = UIColor.green
//或者相同效果的
view.layer.backgroundColor = UIColor.green.cgColor
複製代碼
UILabel: 設置和UIView差很少,有一個區別就是設置label.backgroundColor和layer.cornerRadius不會起效果,須要設置label.layer.backgroundColor和layer.cornerRadius纔會起效果。
//設置圓角邊框
view.layer.cornerRadius = 3.0
view.layer.borderColor = UIColor.red.cgColor
view.layer.borderWidth = 1.0
複製代碼
UITextField: 自帶圓角效果,設置不一樣style便可達到效果。
UITextView: 和UIView的設置方法相同。
UIImageView: UIImageView的狀況比較特殊,上面的幾種方法不能實現圓角,必需要layer.cornerRadius和layer.masksToBounds = YES,才能實現圓角。但這個操做一定會產生離屏渲染,爲了不離屏渲染,經常使用的優化方法有:
重繪圖片,生成一張帶圓角的圖片,而後設置到UIImageView上。
func redrawImage(originImage: UIImage, rectSize: CGSize, cornerRadius: CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale)
if let context = UIGraphicsGetCurrentContext() {
let rect = CGRect(origin: CGPoint.zero, size: rectSize)
let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
context.addPath(path.cgPath)
context.clip()
originImage.draw(in: rect)
context.drawPath(using: .fillStroke)
let roundedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedImage
}
return nil
}
DispatchQueue.global(qos: .default).async {
//在子線程調用redrawImage生成圖片
DispatchQueue.main.async {
//在主線程設置圖片
}
}
複製代碼
在UIImageView上遮蓋一張部分透明的,部分遮擋的圖片,蓋在原來的UIImageView上,曲線實現圖片圓角功能。
//生成中間透明 周圍遮擋的圖片
func getRundedCornerImage(radius: CGFloat, rectSize: CGSize, fillColor: UIColor) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale)
if let currentContext = UIGraphicsGetCurrentContext() {
let rect = CGRect(origin: .zero, size: rectSize)
let outerPath = UIBezierPath(rect: rect)
let innerPath = UIBezierPath(roundedRect: rect,
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: radius, height: radius))
currentContext.setBlendMode(.normal)
fillColor.setFill()
outerPath.fill()
currentContext.setBlendMode(.normal)
innerPath.fill()
let roundedCornerImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return roundedCornerImage
}
return nil
}
//將生成的圖片 加到須要圓角的圖片的上方
複製代碼
設置陰影shadow:
設置shadowPath,能夠解決離屏渲染問題。
self.shadowView.layer.shadowColor = UIColor.gray.cgColor
self.shadowView.layer.shadowOpacity = 0.2
self.shadowView.layer.shadowRadius = 3.0
self.shadowView.layer.shadowOffset = CGSize(width: 1, height: 1)
self.shadowView.layer.shadowPath = UIBezierPath(rect: view.bounds).cgPath
複製代碼
固然和圓角的解決辦法同樣,可使用一張帶陰影的圖來曲線解決問題。
func getRundedCornerShadowImage(originImage: UIImage, rectSize: CGSize, roundedRadius: CGFloat, shadowColor: UIColor, shadowOffset: CGSize, insetX: CGFloat, insetY: CGFloat) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(rectSize, false, UIScreen.main.scale)
if let currentContext = UIGraphicsGetCurrentContext() {
let rect = CGRect(origin: .zero, size: rectSize)
let shadowPath = UIBezierPath(roundedRect: rect.insetBy(dx: insetX, dy: insetY),
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: roundedRadius, height: roundedRadius))
currentContext.setShadow(offset: shadowOffset, blur: roundedRadius, color: shadowColor.cgColor)
currentContext.addPath(shadowPath.cgPath)
shadowPath.fill()
let imagePath = UIBezierPath(roundedRect: rect.insetBy(dx: insetX, dy: insetY),
byRoundingCorners: .allCorners,
cornerRadii: CGSize(width: roundedRadius, height: roundedRadius))
currentContext.addPath(imagePath.cgPath)
currentContext.clip()
originImage.draw(in: rect.insetBy(dx: insetX, dy: insetY))
currentContext.strokePath()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
return nil
}
複製代碼
設置蒙版mask:
設置mask一定會觸發離屏渲染。
mask的過程大體來看是和視圖混合相反的過程,例若有一張圖片,中間有一個圓形空間是透明的,邊緣部分是白色,若是視圖直接疊加在一張頭像上,會呈現出圓形頭型的效果,但若是使用mask則會顯示出中間白邊緣透明的效果。
因此性能敏感的界面中,能夠不使用mask,而使用視圖混合這種對性能影響更小的方式進行操做。
layer.allowsGroupOpacity、layer.allowsEdgeAntialiasing:
這兩個操做對性能並不會形成比較大的影響。
drawRect:
drawRect會形成較大的內存消耗,並會形成離屏渲染,應儘可能避免重寫。
以上招式,少俠可看好了,往後定當好好練習,得到伊人芳心指日可待。
快去找小師妹去罷。
欲知後事如何,且看下回分解: iOS性能優化(中級+): 異步繪製