iOS視圖渲染以及性能優(yōu)化總結(jié)

屏幕的顯示原理


CRT電子槍按照圖片上的方式近她,從上到下、從左到右的方式一行行掃描逢渔,掃描完成之后顯示器就會(huì)顯示一幀的畫面递雀。隨后電子槍會(huì)回到初始位置繼續(xù)下一次掃描(就是黃色虛線部分)项棠。

這里涉及到兩個(gè)概念:水平同步信號(hào)(HSync),垂直同步信號(hào)(VSync)

HSync:當(dāng)電子槍換到新的一行悲雳,準(zhǔn)備開始掃描,顯示器會(huì)發(fā)出一個(gè)HSync

VSync:當(dāng)一幀畫面繪制完成后香追,電子槍回復(fù)到原位合瓢,準(zhǔn)備畫下一幀前,顯示器會(huì)發(fā)出一個(gè)VSync(就是黃色虛線部分)

我們都知道透典,顯示器通常以固定頻率進(jìn)行刷新晴楔,刷新頻率是每秒60幀,平均每幀就16.67ms峭咒。這個(gè)刷新頻率就是 VSync 信號(hào)產(chǎn)生的頻率税弃。

視圖渲染

UIKit框架

UIKit使我們常用的框架

Core Animation翻譯為核心動(dòng)畫,它為圖形渲染和動(dòng)畫提供了基礎(chǔ)凑队,它依賴于OpenGL ES做GPU渲染则果,Core Graphics做CPU渲染。

Graphics Hardware是圖形硬件.

渲染流程

由上圖可知漩氨,要在屏幕上顯示視圖西壮,需要CPU和GPU一起協(xié)作,CPU計(jì)算好顯示的內(nèi)容提交到GPU叫惊,GPU渲染完成后將結(jié)果放到幀緩存區(qū)款青,隨后視頻控制器會(huì)按照 VSync 信號(hào)逐行讀取幀緩沖區(qū)的數(shù)據(jù),經(jīng)過可能的數(shù)模轉(zhuǎn)換傳遞給顯示器顯示霍狰。

iOS使用的是雙緩沖機(jī)制抡草。即GPU 會(huì)預(yù)先渲染好一幀放入一個(gè)緩沖區(qū)內(nèi)(前幀緩存),讓視頻控制器讀取蔗坯,當(dāng)下一幀渲染好后渠牲,GPU 會(huì)直接把視頻控制器的指針指向第二個(gè)緩沖器(后幀緩存)。當(dāng)你視頻控制器已經(jīng)讀完一幀步悠,準(zhǔn)備讀下一幀的時(shí)候签杈,GPU 會(huì)等待顯示器的 VSync 信號(hào)發(fā)出后,前幀緩存和后幀緩存會(huì)瞬間切換,后幀緩存會(huì)變成新的前幀緩存答姥,同時(shí)舊的前幀緩存會(huì)變成新的后幀緩存铣除。

渲染流程

CoreAnimation Pipeline

1.Commit Transaction:通過CPU創(chuàng)建繪制視圖,提交會(huì)話鹦付,包括自己和子數(shù)的layout得狀態(tài)尚粘,圖片的解碼和格式轉(zhuǎn)換等等。詳細(xì)可見下面圖片

2.Render Server:解析提交的子樹狀態(tài)敲长,生成繪制指令

3.GPU執(zhí)行繪制指令郎嫁,進(jìn)行渲染。

4.顯示渲染后的幀緩存的數(shù)據(jù)祈噪。

Commit Transaction

Commit Transaction

Commit Transaction - Layout(布局)

Commit Transaction - Layout

調(diào)用layoutSubviews方法泽铛。

調(diào)用addSubview方法

填充內(nèi)容,數(shù)據(jù)庫查詢

通常會(huì)造成CPU或者I/O瓶頸

Commit Transaction - Display(顯示)

Commit Transaction - Display

通過drawRect:繪制內(nèi)容

String繪制

通常會(huì)造成CPU或者內(nèi)存瓶頸

Commit Transaction - Prepare(準(zhǔn)備提交)

Commit Transaction - Prepare

圖片解碼辑鲤,把圖片從PNG或JPEG等格式中解壓出來盔腔,得到像素?cái)?shù)據(jù)

圖片格式轉(zhuǎn)換,如果GPU不支持這種顏色格式月褥,CPU需要進(jìn)行格式轉(zhuǎn)換

比如應(yīng)用中有一些從網(wǎng)絡(luò)下載的圖片弛随,而GPU恰好不支持這個(gè)格式,這就需要CPU預(yù)先進(jìn)行格式轉(zhuǎn)化宁赤。

Commit Transaction - Commit(提交)


Commit Transaction - Commit

打包layers并且提交到Render Server中

遞歸提交子樹的layers

如果子樹很復(fù)雜舀透,對CPU消耗很大

卡頓產(chǎn)生的原因

在 VSync 信號(hào)到來后,系統(tǒng)圖形服務(wù)會(huì)通過 CADisplayLink 等機(jī)制通知 App决左,App 主線程開始在 CPU 中計(jì)算顯示內(nèi)容盐杂,比如視圖的創(chuàng)建、布局計(jì)算哆窿、圖片解碼链烈、文本繪制等。隨后 CPU 會(huì)將計(jì)算好的內(nèi)容提交到 GPU 去挚躯,由 GPU 進(jìn)行變換强衡、合成、渲染码荔。隨后 GPU 會(huì)把渲染結(jié)果提交到幀緩沖區(qū)去漩勤,等待下一次 VSync 信號(hào)到來時(shí)顯示到屏幕上。由于垂直同步的機(jī)制缩搅,如果在一個(gè) VSync 時(shí)間內(nèi)越败,CPU 或者 GPU 沒有完成內(nèi)容提交,則那一幀就會(huì)被丟棄硼瓣,等待下一次機(jī)會(huì)再顯示究飞,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變置谦。這就是界面卡頓的原因。

從上面的圖中可以看到亿傅,CPU 和 GPU 不論哪個(gè)阻礙了顯示流程媒峡,都會(huì)造成掉幀現(xiàn)象。所以開發(fā)時(shí)葵擎,也需要分別對 CPU 和 GPU 壓力進(jìn)行評估和優(yōu)化谅阿。

CPU 資源消耗原因

1.對象創(chuàng)建:對象的創(chuàng)建會(huì)分配內(nèi)存、調(diào)整屬性酬滤、甚至還有讀取文件等操作签餐,比較消耗 CPU 資源。盡量用輕量的對象代替重量的對象盯串,可以對性能有所優(yōu)化氯檐。比如用CALayer代替UIView,用CATextLayer代替UILabel。另外嘴脾,通過 Storyboard 創(chuàng)建視圖對象時(shí),其資源消耗會(huì)比直接通過代碼創(chuàng)建對象要大非常多

2.對象銷毀:對象的銷毀雖然消耗資源不多蔬墩,但累積起來也是不容忽視的译打。通常當(dāng)容器類持有大量對象時(shí),其銷毀時(shí)的資源消耗就非常明顯拇颅。同樣的奏司,如果對象可以放到后臺(tái)線程去釋放,那就挪到后臺(tái)線程去

3.對象調(diào)整:當(dāng)視圖層次調(diào)整時(shí)樟插,UIView韵洋、CALayer 之間會(huì)出現(xiàn)很多方法調(diào)用與通知,所以在優(yōu)化性能時(shí)黄锤,應(yīng)該盡量避免調(diào)整視圖層次搪缨、添加和移除視圖。

4.布局計(jì)算:視圖布局的計(jì)算是 App 中最為常見的消耗 CPU 資源的地方鸵熟。如果能在后臺(tái)線程提前計(jì)算好視圖布局副编、并且對視圖布局進(jìn)行緩存,那么這個(gè)地方基本就不會(huì)產(chǎn)生性能問題了流强。

5.Autolayout:但是 Autolayout 對于復(fù)雜視圖來說常常會(huì)產(chǎn)生嚴(yán)重的性能問題痹届。隨著視圖數(shù)量的增長,Autolayout 帶來的 CPU 消耗會(huì)呈指數(shù)級上升打月。具體數(shù)據(jù)可以看這個(gè)文章:http://pilky.me/36/队腐。

6.文本計(jì)算:如果一個(gè)界面中包含大量文字,文本的寬高計(jì)算會(huì)占用很大一部分資源奏篙,并且不可避免柴淘。如果你對文本顯示沒有特殊要求,可以參考下 UILabel 內(nèi)部的實(shí)現(xiàn)方式:用 [NSAttributedString boundingRectWithSize:options:context:] 來計(jì)算文本寬高,用 -[NSAttributedString drawWithRect:options:context:] 來繪制文本悠就。另外這兩個(gè)操作盡可能得在后臺(tái)操作千绪。

7.文本渲染:屏幕上能看到的所有文本內(nèi)容控件,包括 UIWebView梗脾,在底層都是通過 CoreText 排版荸型、繪制為 Bitmap 顯示的。常見的文本控件 (UILabel炸茧、UITextView 等)瑞妇,其排版和繪制都是在主線程進(jìn)行的,當(dāng)顯示大量文本時(shí)梭冠,CPU 的壓力會(huì)非常大辕狰。對此解決方案只有一個(gè),那就是自定義文本控件控漠,用 TextKit 或最底層的 CoreText 對文本異步繪制蔓倍。盡管這實(shí)現(xiàn)起來非常麻煩,但其帶來的優(yōu)勢也非常大盐捷,CoreText 對象創(chuàng)建好后偶翅,能直接獲取文本的寬高等信息,避免了多次計(jì)算(調(diào)整 UILabel 大小時(shí)算一遍碉渡、UILabel 繪制時(shí)內(nèi)部再算一遍)聚谁;CoreText 對象占用內(nèi)存較少,可以緩存下來以備稍后多次渲染滞诺。

8.圖片的解碼:當(dāng)你用 UIImage 或 CGImageSource 的那幾個(gè)方法創(chuàng)建圖片時(shí)形导,圖片數(shù)據(jù)并不會(huì)立刻解碼。圖片設(shè)置到 UIImageView 或者 CALayer.contents 中去习霹,并且 CALayer 被提交到 GPU 前朵耕,CGImage 中的數(shù)據(jù)才會(huì)得到解碼。這一步是發(fā)生在主線程的淋叶,并且不可避免憔披。如果想要繞開這個(gè)機(jī)制,常見的做法是在后臺(tái)線程先把圖片繪制到 CGBitmapContext 中爸吮,然后從 Bitmap 直接創(chuàng)建圖片芬膝。目前常見的網(wǎng)絡(luò)圖片庫都自帶這個(gè)功能。例如:可見下面 圖-8

9.圖像的繪制:圖像的繪制通常是指用那些以 CG 開頭的方法把圖像繪制到畫布中形娇,然后從畫布創(chuàng)建圖片并顯示這樣一個(gè)過程锰霜。這個(gè)最常見的地方就是 [UIView drawRect:] 里面了。由于 CoreGraphic 方法通常都是線程安全的桐早,所以圖像的繪制可以很容易的放到后臺(tái)線程進(jìn)行癣缅。詳情也可以見 圖-8

10.圖片的格式:把圖片從PNG或JPEG等格式中解壓出來厨剪,得到像素?cái)?shù)據(jù),友存,如果GPU不支持這種顏色格式祷膳,CPU需要進(jìn)行格式轉(zhuǎn)換,所以屡立,圖片要用GPU支持的圖片數(shù)據(jù)格式直晨。


圖-8

GPU 資源消耗原因

相對于 CPU 來說,GPU 能干的事情比較單一:接收提交的紋理(Texture)和頂點(diǎn)描述(三角形)膨俐,應(yīng)用變換(transform)勇皇、混合并渲染雕凹,然后輸出到屏幕上冀宴。通常你所能看到的內(nèi)容,主要也就是紋理(圖片)和形狀(三角模擬的矢量圖形)兩類习贫。

1.紋理的渲染:所有的 Bitmap乳愉,包括圖片兄淫、文本、柵格化的內(nèi)容蔓姚,最終都要由內(nèi)存提交到顯存捕虽,綁定為 GPU Texture。不論是提交到顯存的過程赂乐,還是 GPU 調(diào)整和渲染 Texture 的過程薯鳍,都要消耗不少 GPU 資源咖气。當(dāng)在較短時(shí)間顯示大量圖片時(shí)(比如 TableView 存在非常多的圖片并且快速滑動(dòng)時(shí))挨措,CPU 占用率很低,GPU 占用非常高崩溪,界面仍然會(huì)掉幀浅役。避免這種情況的方法只能是盡量減少在短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成為一張進(jìn)行顯示伶唯。

當(dāng)圖片過大觉既,超過 GPU 的最大紋理尺寸時(shí),圖片需要先由 CPU 進(jìn)行預(yù)處理乳幸,這對 CPU 和 GPU 都會(huì)帶來額外的資源消耗瞪讼。目前來說,iPhone 4S 以上機(jī)型粹断,紋理尺寸上限都是 4096x4096符欠,更詳細(xì)的資料可以看這里:iosres.com。所以瓶埋,盡量不要讓圖片和視圖的大小超過這個(gè)值希柿。

2.視圖的混合 (Composing):當(dāng)出現(xiàn)重疊的UIView或者CALayer的時(shí)候诊沪,GPU會(huì)去計(jì)算混合部分的像素,所以曾撤,盡可能少的設(shè)置alpha,盡可能多的設(shè)置背景顏色為純色的背景端姚。網(wǎng)上的人說要設(shè)置 opaque = true,但是我個(gè)人認(rèn)為設(shè)置backgroundColor更為有效。

3.圖形的生成:CALayer 的 border挤悉、圓角渐裸、陰影、遮罩(mask)尖啡,CASharpLayer 的矢量圖形顯示橄仆,通常會(huì)觸發(fā)離屏渲染(offscreen rendering),而離屏渲染通常發(fā)生在 GPU 中衅斩。當(dāng)一個(gè)列表視圖中出現(xiàn)大量圓角的 CALayer盆顾,并且快速滑動(dòng)時(shí),可以觀察到 GPU 資源已經(jīng)占滿畏梆,而 CPU 資源消耗很少您宪。這時(shí)界面仍然能正常滑動(dòng)奠涌,但平均幀數(shù)會(huì)降到很低宪巨。為了避免這種情況,可以嘗試開啟 CALayer.shouldRasterize 屬性溜畅,但這會(huì)把原本離屏渲染的操作轉(zhuǎn)嫁到 CPU 上去捏卓。對于只需要圓角的某些場合,也可以用一張已經(jīng)繪制好的圓角圖片覆蓋到原本視圖上面來模擬相同的視覺效果慈格。最徹底的解決辦法怠晴,就是把需要顯示的圖形在后臺(tái)線程繪制為圖片,避免使用圓角浴捆、陰影蒜田、遮罩等屬性。

離屏渲染

離屏渲染指的是GPU在當(dāng)前屏幕緩存區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作选泻。

我們現(xiàn)在看看正常的渲染管道:


正常的渲染管道

1.Command Buffer:OpenGL提交一個(gè)渲染指令給Command Buffer冲粤,然后進(jìn)行GPU渲染。

2.Tiler:調(diào)用頂點(diǎn)著色器页眯,把頂點(diǎn)數(shù)據(jù)進(jìn)行分塊(Tiling)

3.ParameterBuffer:接受分塊完畢的tile和對應(yīng)的渲染參數(shù)

4.Renderer:調(diào)用片元著色器梯捕,進(jìn)行像素渲染

5.Render Buffer: 緩沖區(qū),存儲(chǔ)渲染完畢的像素

離屏渲染的渲染管道(Mask)

離屏渲染管道

1.在第一條渲染管道中窝撵,GPU創(chuàng)建了一個(gè)新的緩存區(qū)來存儲(chǔ)相機(jī)紋理(Texture)的渲染結(jié)果傀顾。

2.在第二條渲染管道中,GPU創(chuàng)建了一個(gè)新的緩存區(qū)來存儲(chǔ)藍(lán)色的蒙版(layer)的渲染結(jié)果忿族。

記住锣笨,前面的兩個(gè)渲染結(jié)果并沒有直接放到Render Buffer中蝌矛,而是臨時(shí)保存

3.在第三條渲染管道中,才把前面的兩個(gè)渲染結(jié)果取出來错英,合并組合入撒,放到Render Buffer中

由此可見,離屏渲染的消耗主要提現(xiàn)在兩個(gè)方法:

1.創(chuàng)建新的緩存區(qū)

2.上下文的切換椭岩。先是從當(dāng)前屏幕切換到離屏茅逮,等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕判哥。而上下文環(huán)境的切換是要付出很大代價(jià)的献雅。

常見的離屏渲染操作有哪些?

1.shouldRasterize(光柵化):這是一個(gè)手動(dòng)開啟離屏渲染的操作塌计,開啟光柵化是將一個(gè)layer預(yù)先渲染成位圖(bitmap)挺身,然后加入緩存中,當(dāng)需要的時(shí)候再從緩存中拿出來锌仅,如果光柵化的元素在100ms內(nèi)沒有被使用就會(huì)被移除章钾,所以,光柵化適用于靜態(tài)內(nèi)容的視圖热芹,也就是內(nèi)部結(jié)構(gòu)和內(nèi)容不發(fā)生變化的視圖贱傀,當(dāng)視圖的內(nèi)容和結(jié)構(gòu)一直在發(fā)生變化,就不應(yīng)該開啟光柵化伊脓,因?yàn)樗鼤?huì)一直創(chuàng)建緩存府寒。

2.masks(遮罩):單獨(dú)設(shè)置cornerRadius或者masksToBounds = true,并不會(huì)引起離屏渲染报腔,他們的合體才會(huì)引起離屏渲染株搔。可以選擇VVebo的做法榄笙,在要添加圓角的視圖上再疊加一個(gè)部分透明的視圖邪狞,只對圓角部分進(jìn)行遮擋祷蝌,遮擋的部分背景與周圍背景相同茅撞。但最徹底的方法還是在后臺(tái)線程中利用CoreGraphic來繪制圖片。另外巨朦,這里說一下UILabel和UITextView設(shè)置圓角的方法米丘,按照下面這么做不會(huì)產(chǎn)生離屏渲染。

label.layer.backgroundColor= aColor

label.layer.cornerRadius=5

3.shadows(陰影):可以通過指定陰影路徑糊啡,避免離屏渲染拄查。imgView.layer.shadowPath =UIBezierPath(rect: imgView.bounds).CGPath

4.EdgeAntialiasing(抗鋸齒):這個(gè)功能貌似在 iOS 8 和 iOS 9 上并不會(huì)觸發(fā)離屏渲染,對性能也沒有什么影響棚蓄,也許這個(gè)功能已經(jīng)被優(yōu)化了堕扶。

5.-(void)drawRect (CPU離屏渲染):如果我們重 寫了drawRect方法碍脏,并且使用Core Graphics的技術(shù)進(jìn)行了繪制操作,就涉及到了CPU渲染稍算。但是我們應(yīng)該盡量避免這種方法典尾,因?yàn)槿绻褂眠@個(gè)方法不恰當(dāng)?shù)脑挄?huì)使內(nèi)存暴漲,這個(gè)在內(nèi)存惡鬼drawRect中做出了詳細(xì)解釋糊探〖毓。總之,能避免重寫drawRect方法就盡可能避免科平。

參考文獻(xiàn)

如何讓iOS 保持界面流暢

http://www.reibang.com/p/748f9abafff8

https://www.objccn.io/issue-3-1/

WWDC2014

離屏渲染

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末褥紫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瞪慧,更是在濱河造成了極大的恐慌髓考,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弃酌,死亡現(xiàn)場離奇詭異绳军,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)矢腻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門门驾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人多柑,你說我怎么就攤上這事奶是。” “怎么了竣灌?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵聂沙,是天一觀的道長。 經(jīng)常有香客問我初嘹,道長及汉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任屯烦,我火速辦了婚禮坷随,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘驻龟。我一直安慰自己温眉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布翁狐。 她就那樣靜靜地躺著类溢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪露懒。 梳的紋絲不亂的頭發(fā)上闯冷,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天砂心,我揣著相機(jī)與錄音,去河邊找鬼蛇耀。 笑死计贰,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蒂窒。 我是一名探鬼主播躁倒,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼洒琢!你這毒婦竟也來了秧秉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衰抑,失蹤者是張志新(化名)和其女友劉穎象迎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呛踊,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砾淌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谭网。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汪厨。...
    茶點(diǎn)故事閱讀 40,127評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖愉择,靈堂內(nèi)的尸體忽然破棺而出劫乱,到底是詐尸還是另有隱情,我是刑警寧澤锥涕,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布衷戈,位于F島的核電站,受9級特大地震影響层坠,放射性物質(zhì)發(fā)生泄漏殖妇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一破花、第九天 我趴在偏房一處隱蔽的房頂上張望谦趣。 院中可真熱鬧,春花似錦旧乞、人聲如沸蔚润。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烦租,卻和暖如春延赌,著一層夾襖步出監(jiān)牢的瞬間除盏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工挫以, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留者蠕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓掐松,卻偏偏與公主長得像踱侣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子大磺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容