iOS 頁(yè)面性能優(yōu)化

前言

在軟件開(kāi)發(fā)領(lǐng)域里經(jīng)常能聽(tīng)到這樣一句話萎羔,“過(guò)早的優(yōu)化是萬(wàn)惡之源”,不要過(guò)早優(yōu)化或者過(guò)度優(yōu)化碳默。我認(rèn)為在編碼過(guò)程中時(shí)刻注意性能影響是有必要的外驱,但凡事都有個(gè)度,不能為了性能耽誤了開(kāi)發(fā)進(jìn)度腻窒。在時(shí)間緊急的情況下我們往往采用“quick and dirty”的方案來(lái)快速出成果昵宇,后面再迭代優(yōu)化,即所謂的敏捷開(kāi)發(fā)儿子。與之相對(duì)應(yīng)的是傳統(tǒng)軟件開(kāi)發(fā)中的瀑布流開(kāi)發(fā)流程瓦哎。

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

在 iOS 系統(tǒng)中,圖像內(nèi)容展示到屏幕的過(guò)程需要 CPU 和 GPU 共同參與柔逼。CPU 負(fù)責(zé)計(jì)算顯示內(nèi)容蒋譬,比如視圖的創(chuàng)建、布局計(jì)算愉适、圖片解碼犯助、文本繪制等。隨后 CPU 會(huì)將計(jì)算好的內(nèi)容提交到 GPU 去维咸,由 GPU 進(jìn)行變換剂买、合成、渲染癌蓖。之后 GPU 會(huì)把渲染結(jié)果提交到幀緩沖區(qū)去瞬哼,等待下一次 VSync 信號(hào)到來(lái)時(shí)顯示到屏幕上。由于垂直同步的機(jī)制租副,如果在一個(gè) VSync 時(shí)間內(nèi)坐慰,CPU 或者 GPU 沒(méi)有完成內(nèi)容提交,則那一幀就會(huì)被丟棄用僧,等待下一次機(jī)會(huì)再顯示结胀,而這時(shí)顯示屏?xí)A糁暗膬?nèi)容不變。這就是界面卡頓的原因责循。

因此糟港,我們需要平衡 CPU 和 GPU 的負(fù)荷避免一方超負(fù)荷運(yùn)算。為了做到這一點(diǎn)沼死,我們首先得了解 CPU 和 GPU 各自負(fù)責(zé)哪些內(nèi)容着逐。

上面的圖展示了 iOS 系統(tǒng)下各個(gè)模塊所處的位置,下面我們?cè)倬唧w看一下 CPU 和 GPU 對(duì)應(yīng)了哪些操作。

CPU 消耗型任務(wù)

布局計(jì)算

布局計(jì)算是 iOS 中最為常見(jiàn)的消耗 CPU 資源的地方耸别,如果視圖層級(jí)關(guān)系比較復(fù)雜沸手,計(jì)算出所有圖層的布局信息就會(huì)消耗一部分時(shí)間虐秦。因此我們應(yīng)該盡量提前計(jì)算好布局信息,然后在合適的時(shí)機(jī)調(diào)整對(duì)應(yīng)的屬性。還要避免不必要的更新瘦癌,只在真正發(fā)生了布局改變時(shí)再更新嗦玖。

對(duì)象創(chuàng)建

對(duì)象創(chuàng)建過(guò)程伴隨著內(nèi)存分配澜掩、屬性設(shè)置贯涎、甚至還有讀取文件等操作,比較消耗 CPU 資源蠢沿。盡量用輕量的對(duì)象代替重量的對(duì)象伸头,可以對(duì)性能有所優(yōu)化。比如 CALayer 比 UIView 要輕量許多舷蟀,如果視圖元素不需要響應(yīng)觸摸事件恤磷,用 CALayer 會(huì)更加合適。

通過(guò) Storyboard 創(chuàng)建視圖對(duì)象還會(huì)涉及到文件反序列化操作野宜,其資源消耗會(huì)比直接通過(guò)代碼創(chuàng)建對(duì)象要大非常多扫步,在性能敏感的界面里,Storyboard 并不是一個(gè)好的技術(shù)選擇匈子。

對(duì)于列表類型的頁(yè)面河胎,還可以參考 UITableView 的復(fù)用機(jī)制。每次要初始化 View 對(duì)象時(shí)先根據(jù) identifier 從緩存池里取虎敦,能取到就復(fù)用這個(gè) View 對(duì)象游岳,取不到再真正執(zhí)行初始化過(guò)程≡滑動(dòng)屏幕時(shí)吭历,會(huì)將滑出屏幕外的 View 對(duì)象根據(jù) identifier 放入緩存池,新進(jìn)入屏幕可見(jiàn)范圍內(nèi)的 View 又根據(jù)前面的規(guī)則來(lái)決定是否要真正初始化擂橘。

Autolayout

Autolayout 是蘋(píng)果在 iOS6 之后新引入的布局技術(shù),在大多數(shù)情況下這一技術(shù)都能大大提升開(kāi)發(fā)速度摩骨,特別是在需要處理多語(yǔ)言時(shí)通贞。比如阿拉伯語(yǔ)下布局是從右往左,通過(guò) Autolayout 設(shè)置 leading 和 trailing 即可恼五。

但是 Autolayout 對(duì)于復(fù)雜視圖來(lái)說(shuō)常常會(huì)產(chǎn)生嚴(yán)重的性能問(wèn)題昌罩,對(duì)于性能敏感的頁(yè)面建議還是使用手動(dòng)布局的方式,并控制好刷新頻率灾馒,做到真正需要調(diào)整布局時(shí)再重新布局茎用。

文本計(jì)算

如果一個(gè)界面中包含大量文本(比如微博、微信朋友圈等),文本的寬高計(jì)算會(huì)占用很大一部分資源轨功,并且不可避免旭斥。

一個(gè)比較常見(jiàn)的場(chǎng)景是在 UITableView 中,heightForRowAtIndexPath這個(gè)方法會(huì)被頻繁調(diào)用古涧,即使不是耗時(shí)的計(jì)算在調(diào)用次數(shù)多了之后也會(huì)帶來(lái)性能損耗垂券。這里的優(yōu)化就是盡量避免每次都重新進(jìn)行文本的行高計(jì)算,可以在獲取到 Model 數(shù)據(jù)后就根據(jù)文本內(nèi)容計(jì)算好布局信息羡滑,然后將這份布局信息作為一個(gè)屬性保存到對(duì)應(yīng)的 Model 中菇爪,這樣在 UITableView 的回調(diào)中就可以直接使用 Model 中的屬性,減少了文本的計(jì)算柒昏。

文本渲染

屏幕上能看到的所有文本內(nèi)容控件凳宙,包括 UIWebView,在底層都是通過(guò) CoreText 排版职祷、繪制為 Bitmap 顯示的近速。常見(jiàn)的文本控件 (UILabel、UITextView 等)堪旧,其排版和繪制都是在主線程進(jìn)行的削葱,當(dāng)顯示大量文本時(shí),CPU 的壓力會(huì)非常大淳梦。

這一部分的性能優(yōu)化就需要我們放棄使用系統(tǒng)提供的上層控件轉(zhuǎn)而直接使用 CoreText 進(jìn)行排版控制析砸。

Wherever possible, try to avoid making changes to the frame of a view that contains text, because it will cause the text to be redrawn. For example, if you need to display a static block of text in the corner of a layer that frequently changes size, put the text in a sublayer instead.

上面這段話翻譯過(guò)來(lái)的意思就是說(shuō)包含文本的視圖在改變布局時(shí)會(huì)觸發(fā)文本的重新渲染,對(duì)于靜態(tài)文本我們應(yīng)該盡量減少它所在視圖的布局修改爆袍。

圖像的繪制

圖像的繪制通常是指用那些以 CG 開(kāi)頭的方法把圖像繪制到畫(huà)布中首繁,然后從畫(huà)布創(chuàng)建圖片并顯示的過(guò)程。前面的模塊圖里介紹了 CoreGraphic 是作用在 CPU 之上的陨囊,因此調(diào)用 CG 開(kāi)頭的方法消耗的是 CPU 資源弦疮。我們可以將繪制過(guò)程放到后臺(tái)線程,然后在主線程里將結(jié)果設(shè)置到 layer 的 contents 中蜘醋。代碼如下:

- (void)display {

?? ?dispatch_async(backgroundQueue, ^{

? ? ? ? CGContextRef ctx = CGBitmapContextCreate(...);

// draw in context...CGImageRef img = CGBitmapContextCreateImage(ctx);? ? ? ? CFRelease(ctx);

? ? ? ? dispatch_async(mainQueue, ^{

? ? ? ? ? ? layer.contents = img;

? ? ? ? });

? ? });

}

圖片的解碼

Once an image file has been loaded, it must then be decompressed. This decompression can be a computationally complex task and take considerable time. The decompressed image will also use substantially more memory than the original.

圖片被加載后需要解碼胁塞,圖片的解碼是一個(gè)復(fù)雜耗時(shí)的過(guò)程,并且需要占用比原始圖片還多的內(nèi)存資源压语。

為了節(jié)省內(nèi)存啸罢,iOS 系統(tǒng)會(huì)延遲解碼過(guò)程, 在圖片被設(shè)置到 layer 的 contents 屬性或者設(shè)置成 UIImageView 的 image 屬性后才會(huì)執(zhí)行解碼過(guò)程胎食,但是這兩個(gè)操作都是在主線程進(jìn)行扰才,還是會(huì)帶來(lái)性能問(wèn)題。

這里多提一點(diǎn)厕怜,常用的 UIImage 加載方法有imageNamed和imageWithContentsOfFile衩匣。其中imageNamed加載圖片后會(huì)馬上解碼蕾总,并且系統(tǒng)會(huì)將解碼后的圖片緩存起來(lái),但是這個(gè)緩存策略是不公開(kāi)的琅捏,我們無(wú)法知道圖片什么時(shí)候會(huì)被釋放生百。因此在一些性能敏感的頁(yè)面,我們還可以用 static 變量 hold 住imageNamed加載到的圖片避免被釋放掉午绳,以空間換時(shí)間的方式來(lái)提高性能置侍。

GPU消耗型任務(wù)

相對(duì)于 CPU 來(lái)說(shuō),GPU 能干的事情比較單一:接收提交的紋理(Texture)和頂點(diǎn)描述(三角形)拦焚,應(yīng)用變換(transform)蜡坊、混合并渲染,然后輸出到屏幕上赎败。寬泛的說(shuō)秕衙,大多數(shù) CALayer 的屬性都是用 GPU 來(lái)繪制。

以下一些操作會(huì)降低 GPU 繪制的性能僵刮,

大量幾何結(jié)構(gòu)

所有的 Bitmap据忘,包括圖片、文本搞糕、柵格化的內(nèi)容勇吊,最終都要由內(nèi)存提交到顯存,綁定為 GPU Texture窍仰。不論是提交到顯存的過(guò)程汉规,還是 GPU 調(diào)整和渲染 Texture 的過(guò)程,都要消耗不少 GPU 資源驹吮。當(dāng)在較短時(shí)間顯示大量圖片時(shí)(比如 TableView 存在非常多的圖片并且快速滑動(dòng)時(shí))针史,CPU 占用率很低,GPU 占用非常高碟狞,界面仍然會(huì)掉幀啄枕。避免這種情況的方法只能是盡量減少在短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成為一張進(jìn)行顯示族沃。

另外當(dāng)圖片過(guò)大频祝,超過(guò) GPU 的最大紋理尺寸時(shí),圖片需要先由 CPU 進(jìn)行預(yù)處理竭业,這對(duì) CPU 和 GPU 都會(huì)帶來(lái)額外的資源消耗智润。

視圖的混合

當(dāng)多個(gè)視圖(或者說(shuō) CALayer)重疊在一起顯示時(shí),GPU 會(huì)首先把他們混合到一起未辆。如果視圖結(jié)構(gòu)過(guò)于復(fù)雜,混合的過(guò)程也會(huì)消耗很多 GPU 資源锯玛。為了減輕這種情況的 GPU 消耗咐柜,應(yīng)用應(yīng)當(dāng)盡量減少視圖數(shù)量和層次兼蜈,并且減少不必要的透明視圖。

離屏渲染

離屏渲染是指圖層在被顯示之前是在當(dāng)前屏幕緩沖區(qū)以外開(kāi)辟的一個(gè)緩沖區(qū)進(jìn)行渲染操作拙友。

離屏渲染需要多次切換上下文環(huán)境:先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen)为狸;等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕遗契,而上下文環(huán)境的切換是一項(xiàng)高開(kāi)銷的動(dòng)作辐棒。

會(huì)造成 offscreen rendering 的原因有:

陰影(UIView.layer.shadowOffset/shadowRadius/…)

圓角(當(dāng) UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一起使用時(shí))

圖層蒙板

開(kāi)啟光柵化(shouldRasterize = true)

使用陰影時(shí)同時(shí)設(shè)置 shadowPath 就能避免離屏渲染大大提升性能,后面會(huì)有一個(gè) Demo 來(lái)演示牍蜂;圓角觸發(fā)的離屏渲染可以用 CoreGraphics 將圖片處理成圓角來(lái)避免漾根。

CALayer 有一個(gè) shouldRasterize 屬性,將這個(gè)屬性設(shè)置成 true 后就開(kāi)啟了光柵化鲫竞。開(kāi)啟光柵化后會(huì)將圖層繪制到一個(gè)屏幕外的圖像辐怕,然后這個(gè)圖像將會(huì)被緩存起來(lái)并繪制到實(shí)際圖層的 contents 和子圖層,對(duì)于有很多的子圖層或者有復(fù)雜的效果應(yīng)用从绘,這樣做就會(huì)比重繪所有事務(wù)的所有幀來(lái)更加高效寄疏。但是光柵化原始圖像需要時(shí)間,而且會(huì)消耗額外的內(nèi)存僵井。

光柵化也會(huì)帶來(lái)一定的性能損耗陕截,是否要開(kāi)啟就要根據(jù)實(shí)際的使用場(chǎng)景了,圖層內(nèi)容頻繁變化時(shí)不建議使用批什。最好還是用 Instruments 比對(duì)開(kāi)啟前后的 FPS 來(lái)看是否起到了優(yōu)化效果农曲。

注意:

shouldRasterize = true 時(shí)記得同時(shí)設(shè)置 rasterizationScale

Instruments 使用

Instruments 是一系列工具集,我們這里只演示 Core Animation 的使用渊季。在 Core Animation 選項(xiàng)右下方會(huì)看到如下選項(xiàng)朋蔫,

Color Blended Layers

這個(gè)選項(xiàng)選項(xiàng)基于渲染程度對(duì)屏幕中的混合區(qū)域進(jìn)行綠到紅的高亮顯示,越紅表示性能越差却汉,會(huì)對(duì)幀率等指標(biāo)造成較大的影響驯妄。紅色通常是由于多個(gè)半透明圖層疊加引起。

Color Hits Green and Misses Red

當(dāng) UIView.layer.shouldRasterize = YES 時(shí)合砂,耗時(shí)的圖片繪制會(huì)被緩存青扔,并當(dāng)做一個(gè)簡(jiǎn)單的扁平圖片來(lái)呈現(xiàn)。這時(shí)候翩伪,如果頁(yè)面的其他區(qū)塊(比如 UITableViewCell 的復(fù)用)使用緩存直接命中微猖,就顯示綠色,反之缘屹,如果不命中凛剥,這時(shí)就顯示紅色。紅色越多轻姿,性能越差犁珠。因?yàn)闁鸥窕删彺娴倪^(guò)程是有開(kāi)銷的逻炊,如果緩存能被大量命中和有效使用,則總體上會(huì)降低開(kāi)銷犁享,反之則意味著要頻繁生成新的緩存余素,這會(huì)讓性能問(wèn)題雪上加霜。

Color Copied Images

對(duì)于 GPU 不支持的色彩格式的圖片只能由 CPU 來(lái)處理炊昆,把這樣的圖片標(biāo)為藍(lán)色桨吊。藍(lán)色越多,性能越差凤巨。

Color Immediately

通常 Core Animation Instruments 以每毫秒 10 次的頻率更新圖層調(diào)試顏色视乐。對(duì)某些效果來(lái)說(shuō),這顯然太慢了磅甩。這個(gè)選項(xiàng)就可以用來(lái)設(shè)置每幀都更新(可能會(huì)影響到渲染性能炊林,而且會(huì)導(dǎo)致幀率測(cè)量不準(zhǔn),所以不要一直都設(shè)置它)卷要。

Color Misaligned Images

這個(gè)選項(xiàng)檢查了圖片是否被縮放渣聚,以及像素是否對(duì)齊。被放縮的圖片會(huì)被標(biāo)記為黃色僧叉,像素不對(duì)齊則會(huì)標(biāo)注為紫色奕枝。黃色、紫色越多瓶堕,性能越差隘道。

Color Offscreen-Rendered Yellow

這個(gè)選項(xiàng)會(huì)把那些離屏渲染的圖層顯示為黃色。黃色越多郎笆,性能越差谭梗。這些顯示為黃色的圖層很可能需要用 shadowPath 或者 shouldRasterize 來(lái)優(yōu)化。

Color OpenGL Fast Path Blue

這個(gè)選項(xiàng)會(huì)把任何直接使用 OpenGL 繪制的圖層顯示為藍(lán)色宛蚓。藍(lán)色越多激捏,性能越好。如果僅僅使用 UIKit 或者 Core Animation 的 API凄吏,那么不會(huì)有任何效果远舅。

Flash Updated Regions

這個(gè)選項(xiàng)會(huì)把重繪的內(nèi)容顯示為黃色。不該出現(xiàn)的黃色越多痕钢,性能越差图柏。通常我們希望只是更新的部分被標(biāo)記完黃色。

演示

上述幾個(gè)選項(xiàng)中常用來(lái)檢測(cè)性能的是 Color Blended Layers任连、Offscreen-Rendered Yellow 和 Color Hits Green and Misses Red蚤吹。下面我重點(diǎn)演示一下離屏渲染和光柵化的檢測(cè),寫(xiě)了一個(gè)簡(jiǎn)單的 Demo 設(shè)置了陰影效果随抠,代碼如下:

view.layer.shadowOffset = CGSizeMake(1,1);

view.layer.shadowOpacity =1.0;? ??

view.layer.shadowRadius =2.0;

view.layer.shadowColor = [UIColor blackColor].CGColor;

//? ? view.layer.shadowPath = CGPathCreateWithRect(CGRectMake(0, 0, 50, 50), NULL);

shadowPath沒(méi)有設(shè)置時(shí)用 Instruments 檢測(cè) FPS 基本在 20 以下(iPhone6設(shè)備)距辆,設(shè)置了shadowPath后基本維持在 55 左右余佃,性能提升十分明顯暮刃。

下面來(lái)看一下光柵化的檢測(cè)跨算,代碼如下,

view.layer.shouldRasterize = YES;

view.layer.rasterizationScale = [UIScreen mainScreen].scale;

勾選 Color Hits Green and Misses Red 選項(xiàng)后顯示如下:

我們可以看到在靜止時(shí)緩存都生效了椭懊,在快速滑動(dòng)時(shí)緩存基本不起作用诸蚕,因此是否要開(kāi)啟光柵化還是得根據(jù)具體場(chǎng)景,用 Instruments 檢測(cè)開(kāi)啟前后的性能來(lái)決定氧猬。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末背犯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子盅抚,更是在濱河造成了極大的恐慌漠魏,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妄均,死亡現(xiàn)場(chǎng)離奇詭異柱锹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)丰包,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)禁熏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人邑彪,你說(shuō)我怎么就攤上這事瞧毙。” “怎么了寄症?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵宙彪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我有巧,道長(zhǎng)释漆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任剪决,我火速辦了婚禮灵汪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柑潦。我一直安慰自己享言,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布渗鬼。 她就那樣靜靜地躺著览露,像睡著了一般。 火紅的嫁衣襯著肌膚如雪譬胎。 梳的紋絲不亂的頭發(fā)上差牛,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天命锄,我揣著相機(jī)與錄音,去河邊找鬼偏化。 笑死脐恩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的侦讨。 我是一名探鬼主播驶冒,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼韵卤!你這毒婦竟也來(lái)了骗污?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤沈条,失蹤者是張志新(化名)和其女友劉穎需忿,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蜡歹,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屋厘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了季稳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片擅这。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖景鼠,靈堂內(nèi)的尸體忽然破棺而出仲翎,到底是詐尸還是另有隱情,我是刑警寧澤铛漓,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布溯香,位于F島的核電站,受9級(jí)特大地震影響浓恶,放射性物質(zhì)發(fā)生泄漏玫坛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一包晰、第九天 我趴在偏房一處隱蔽的房頂上張望湿镀。 院中可真熱鬧,春花似錦伐憾、人聲如沸勉痴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蒸矛。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雏掠,已是汗流浹背斩祭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乡话,地道東北人摧玫。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蚊伞,于是被迫代替她去往敵國(guó)和親席赂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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