iOS繪制和渲染

繪制和渲染的流程

繪制和渲染流程

運(yùn)行一段動(dòng)畫(huà)的過(guò)程可以分為6個(gè)階段:

1> 布局 - 為視圖/圖層準(zhǔn)備層級(jí)關(guān)系拖陆,以及設(shè)置圖層屬性(位置抹恳,背景色,邊框等等)的階段橙困。
2> 顯示 - 圖層的寄宿圖片被繪制的階段瞧掺。繪制涉及到-drawRect:和-drawLayer:inContext:方法的調(diào)用。
3> 準(zhǔn)備 - Image decoding, Image conversion(如果圖片類(lèi)型不是GPU所支持的纷宇,需要對(duì)圖片進(jìn)行轉(zhuǎn)換)夸盟。
4> 提交 - Core Animation打包所有的圖層和動(dòng)畫(huà),然后通過(guò)IPC(進(jìn)程內(nèi)通信)發(fā)送到渲染服務(wù)(render server像捶,一個(gè)單獨(dú)管理動(dòng)畫(huà)和圖層組合的一個(gè)系統(tǒng)進(jìn)程)上陕。這個(gè)步驟是遞歸的,所以如果layer tree如果比較復(fù)雜此步驟代價(jià)比較高拓春。

上面4個(gè)步驟發(fā)生在自己的應(yīng)用程序內(nèi)部释簿,動(dòng)畫(huà)顯示到屏幕之前還有2個(gè)步驟的工作:
5> 對(duì)所有圖層屬性計(jì)算中間值,設(shè)置OpenGL幾何形狀來(lái)執(zhí)行渲染硼莽。
6> 在屏幕上渲染可見(jiàn)的三角形庶溶。

前5個(gè)階段都在軟件層面處理(通過(guò)CPU),只有最后一個(gè)階段被GPU執(zhí)行懂鸵。6個(gè)階段中只有布局顯示兩個(gè)階段是可以被我們控制的偏螺,Core Animation框架處理剩下的事務(wù)。

CPU vs GPU

在16.67ms內(nèi)準(zhǔn)備好需要渲染的幀

CPU和GPU在屏幕上顯示內(nèi)容扮演了重要的角色匆光,為了達(dá)到60fps套像,CPU和GPU需要在1/60=16.67ms內(nèi)完成各自的工作。在優(yōu)化iOS繪制和渲染過(guò)程中终息,需要從CPU和GPU兩方面入手夺巩,確認(rèn)是哪一部分達(dá)到了性能瓶頸影響了繪制效率。并且在可控制的布局和顯示階段周崭,決定哪些由CPU執(zhí)行柳譬,哪些交給GPU去做。

影響CPU使用率的操作

布局的計(jì)算

如果視圖層級(jí)過(guò)于復(fù)雜续镇,當(dāng)視圖呈現(xiàn)或者修改的時(shí)候美澳,計(jì)算圖層會(huì)消耗一部分時(shí)間。(UITableView的動(dòng)態(tài)計(jì)算cell高度)

解壓圖片

圖片繪制到屏幕上之前,必須把它擴(kuò)展成完整的未解壓的尺寸人柿。

圖片轉(zhuǎn)換

Session 419 WWDC 2014[3]中提到:“If an image is in a color format that the GPU can not directly work with, it will be converted in the CPU.”
也就是說(shuō)圖片的顏色格式不是32bit柴墩,那么CPU會(huì)先進(jìn)行顏色格式轉(zhuǎn)換,然后GPU才會(huì)進(jìn)行渲染凫岖。最好直接提供32bit顏色格式的圖片江咳,避免轉(zhuǎn)換,或者在非主線程中進(jìn)行格式轉(zhuǎn)換哥放。

可以通過(guò)Core Animation Instruments的Color Copied Images選項(xiàng)進(jìn)行圖片顏色格式檢測(cè)歼指。

繪制
使用CALayer進(jìn)行繪制:

實(shí)現(xiàn)了UIView的-drawRect:或者CALayerDelegate的-drawLayer:inContext:方法,為了支持對(duì)圖層內(nèi)容的任意繪制甥雕,Core Animation必須創(chuàng)建一個(gè)圖層寬*圖層高*4字節(jié)大小的寄宿圖踩身,寬高的單位均為像素

CALayer的contents屬性就對(duì)應(yīng)于寄宿圖社露,寄宿圖是通過(guò)backing store來(lái)保存的挟阻。如果沒(méi)有實(shí)現(xiàn)-drawRect:方法,CALayer的contents為空的峭弟。(通過(guò)po CALayer會(huì)發(fā)現(xiàn)附鸽,實(shí)現(xiàn)了-drawRect:的CALayer的contents有內(nèi)容,反之則沒(méi)有瞒瘸。)

比如在iPhoneX的模擬器上創(chuàng)建一個(gè)沒(méi)有實(shí)現(xiàn)drawRect的5000*5000的視圖:

DrawRectView *drawRectView = [[DrawRectView alloc] initWithFrame:CGRectMake(0, 0, 5000, 5000)];
[self.view addSubview:drawRectView];

// 實(shí)現(xiàn)-drawRect:后的CALayer狀態(tài)
(lldb) po 0x604000239700
<CALayer:0x604000239700; 
position = CGPoint (2500 2500); 
bounds = CGRect (0 0; 5000 5000); 
delegate = <DrawRectView: 0x7fb500410ea0; 
frame = (0 0; 5000 5000); 
layer = <CALayer: 0x604000239700>>; 
contents = <CABackingStore 0x7fb500702100 (buffer [15000 15000] BGRX8888)>; opaque = YES; 
allowsGroupOpacity = YES;
opacity = 1; 
rasterizationScale = 3; 
contentsScale = 3>

此時(shí)使用了內(nèi)存41M坷备;當(dāng)在DrawRectView中實(shí)現(xiàn)一個(gè)空的-drawRect:方法時(shí),此時(shí)內(nèi)存還是41M情臭;當(dāng)給drawRectView設(shè)置背景顏色后省撑,此時(shí)內(nèi)存暴漲到了899M

使用CATileLayer進(jìn)行繪制:

在DrawRectView.m中保留-DrawRect:的同時(shí)加入如下代碼:

+ (Class)layerClass {
    return [CATiledLayer class];
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [(CATiledLayer *)self.layer setTileSize:CGSizeMake(100 * self.contentScaleFactor,
                                                           100 * self.contentScaleFactor)];
    }
    return self;
}

<CATiledLayer:0x6000004257c0; 
position = CGPoint (2500 2500); 
bounds = CGRect (0 0; 5000 5000); 
delegate = <DrawRectView: 0x7fe31bf19e90; 
frame = (0 0; 5000 5000); 
layer = <CATiledLayer: 0x6000004257c0>>; 
contents = <CAImageProvider 0x7fe31bf04940: 15000 x 15000>; 
opaque = YES; 
canDrawConcurrently = YES; 
allowsGroupOpacity = YES; 
opacity = 1; 
tileSize = CGSize (300 300); 
rasterizationScale = 3; 
contentsScale = 3>

內(nèi)存使用率又會(huì)降低到41M俯在,CATiledLayer中沒(méi)有寄宿圖竟秫,contents部分是CAImageProvider。

使用CAShapeLayer進(jìn)行繪制:

1> 渲染快速跷乐。CAShapeLayer使用了硬件加速肥败,繪制同一圖形會(huì)比用Core Graphics快很多。
2> 高效使用內(nèi)存劈猿。一個(gè)CAShapeLayer不需要像普通CALayer一樣創(chuàng)建一個(gè)寄宿圖形,所以無(wú)論有多大潮孽,都不會(huì)占用太多的內(nèi)存揪荣。
3> 不會(huì)被圖層邊界剪裁掉。
4> 不會(huì)出現(xiàn)像素化往史。

一旦繪制結(jié)束之后仗颈,數(shù)據(jù)通過(guò)IPC傳到渲染服務(wù)。圖層每次重繪的時(shí)候都需要抹掉分配的內(nèi)存來(lái)重新分配,在此基礎(chǔ)上挨决,Core Graphics繪制就會(huì)變得十分緩慢请祖,所以提高繪制性能時(shí)需要盡量避免去繪制。

像素對(duì)齊

建議總是將layer對(duì)象的寬高設(shè)置成整數(shù)脖祈,盡管可以設(shè)置成浮點(diǎn)數(shù)肆捕,但是由于會(huì)根據(jù)layer的bounds來(lái)創(chuàng)建位圖圖片,Core Animation最終會(huì)將layer寬高轉(zhuǎn)換成整數(shù)[4]盖高。

Core Animation Instruments中的Color Misaligned Images選項(xiàng)會(huì)做出一些標(biāo)記慎陵。
洋紅色: UIView的frame像素不對(duì)齊,即不能換算成整數(shù)像素值喻奥。
黃色:UIImageView的圖片像素大小與其frame.size不對(duì)齊席纽,圖片發(fā)生了縮放。

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 100, 400.1000023, 100.222221110000001)];
label.text = @"{{100, 100}, {100.1000023, 400.222221110000001}}";
[self.view addSubview:label];

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 250, 200, 300)];
imageView.image = [UIImage imageNamed:@"test.png"];
[self.view addSubview:imageView];
像素不對(duì)齊標(biāo)記
iPhoneX適配遇到的像素對(duì)齊問(wèn)題

如果是使用CATileLayer進(jìn)行繪制撞蚕,如果是水平方向等分的方式進(jìn)行繪制润梯,如下所示:

+ (Class)layerClass {
    return [CATiledLayer class];
}

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        CGFloat width = [UIScreen mainScreen].bounds.size.width / 2.0f;
        [(CATiledLayer *)self.layer setTileSize:CGSizeMake(width * self.contentScaleFactor,
                                                           100 * self.contentScaleFactor)];
    }
    return self;
}

- (void)drawRect:(CGRect)rect {
    NSLog(@"%@", NSStringFromCGRect(rect));
}

按照我們的期望,-drawRect:中打印的應(yīng)該是"{{0,0}, {187.5, 100}}和{{187.5,0},{187.5, 100}}"之類(lèi)的結(jié)果甥厦,但是真實(shí)結(jié)果卻是這樣的:

2018-01-25 11:12:34.508418+0800  {{187.33333333333331, 0}, {187.33333333333331, 100}}
2018-01-25 11:12:34.509249+0800  {{374.66666666666663, 300}, {187.33333333333337, 100}}
2018-01-25 11:12:34.509249+0800  {{187.33333333333331, 300}, {187.33333333333331, 100}}
2018-01-25 11:12:34.509921+0800  {{187.33333333333331, 400}, {187.33333333333331, 100}}
2018-01-25 11:12:34.509921+0800  {{374.66666666666663, 400}, {187.33333333333337, 100}}
2018-01-25 11:12:34.510676+0800  {{187.33333333333331, 500}, {187.33333333333331, 100}}

可以推測(cè)出纺铭,設(shè)置分塊圖片的寬度為375 / 2 * 3(PixelsPerPoint) = 562.5(像素)。分塊繪制的圖片在轉(zhuǎn)換成位圖時(shí)寬度轉(zhuǎn)換為整數(shù)變成562像素矫渔。在-drawRect:中參數(shù)rect對(duì)應(yīng)的分塊區(qū)域的寬度為:562 / 3 = 187.33333彤蔽,而不是375/2=187.5
由于iPhoneX之前的機(jī)型水平分辨率都是偶數(shù)庙洼,所以水平均分分塊繪制不會(huì)出現(xiàn)問(wèn)題顿痪。但是iPhoneX的分辨率是1125*2436,水平方向的像素是奇數(shù)油够,所以可能會(huì)出現(xiàn)一些奇怪的現(xiàn)象蚁袭。所以涉及到像素操作的代碼要確保最后得到的像素單位是整數(shù)。

影響GPU使用率的操作

通過(guò)Instruments GPU Driver查看GPU使用率:

通過(guò)GPU Driver查看GPU使用率
圖層混合

layer的混合涉及到顏色的計(jì)算石咬,兩個(gè)layer混合后每個(gè)混合后的像素顏色計(jì)算公式為:R = S + D * (1 - Sa)揩悄,(Source(top),Destination(lower))鬼悠。如果Source(top)是不透明的删性,那么R = S。

如果CALayer上的opaque屬性為YES焕窝,那么該layer就是不透明蹬挺,GPU不會(huì)做任何合成,只是簡(jiǎn)單的層拷貝它掂。CALayer上opaque的默認(rèn)值是NO巴帮,UIView的alpha默認(rèn)為1。

修改opaque屬性只是會(huì)修改Core Animation的backing store,如果CALayer的contents屬性是一張帶有alpha通道的圖片的話榕茧,圖片仍然會(huì)保留其alpha通道而忽略掉opaque屬性的值[CALayer文檔]垃沦。比如UIImageView雖然有CALayer,但是該圖層并沒(méi)有backing store用押,而是使用一個(gè)CGImageRef作為它的內(nèi)容肢簿,渲染服務(wù)會(huì)把圖片的數(shù)據(jù)繪制到幀緩沖區(qū)[2]

通過(guò)開(kāi)啟Core Animation Instruments的Color Blended Layers選項(xiàng)來(lái)檢測(cè)圖層混合只恨,發(fā)生圖層混合會(huì)顯示紅色译仗。

圖層混合檢測(cè)

UILabel在使用時(shí)避免圖層混合的設(shè)置方法:

self.likeLabel.layer.masksToBounds = YES;
self.likeLabel.backgroundColor = SNBThemeColor(SNB_BLK_LV9_COL, YES);
離屏渲染

GPU的屏幕渲染方式有兩種:
1> On-Screen Rendering即當(dāng)前屏幕渲染,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行官觅。
2> Off-Screen Rendering即離屏渲染纵菌,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開(kāi)辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。

離屏渲染的代價(jià):
1> 創(chuàng)建新的緩沖區(qū)休涤。
2> 上下文切換咱圆。離屏渲染的過(guò)程中,會(huì)發(fā)生上下文:從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen)功氨;等到離屏渲染結(jié)束以后序苏,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上,又需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕捷凄。

為什么需要離屏渲染忱详?
一般情況下,OpenGL會(huì)將提交到渲染服務(wù)(Render Server)的動(dòng)畫(huà)直接渲染跺涤,但是對(duì)于一些復(fù)雜的圖像動(dòng)畫(huà)不能直接進(jìn)行疊加渲染顯示匈睁,而是需要根據(jù)Command Buffer分通道進(jìn)行渲染之后再組合,在組合過(guò)程中桶错,有些渲染通道不會(huì)直接顯示航唆,而這些沒(méi)有直接顯示在屏幕上的通道就是Offscreen Render Pass[3][6]

Offscreen Render需要更多的渲染通道院刁,而不同的渲染通道切換需要耗費(fèi)一定的時(shí)間糯钙,這個(gè)時(shí)間內(nèi)GPU會(huì)閑置,當(dāng)通道達(dá)到一定數(shù)量退腥,對(duì)性能會(huì)有較大的影響任岸。

比如,UIBlurEffect的GPU渲染過(guò)程[3]

UIBlurEffect效果實(shí)現(xiàn)
通道切換間GPU的閑置

UIBlurEffect需要5個(gè)通道才能合成最終的效果圖狡刘,每一個(gè)通道需要上一個(gè)通道的輸出作為輸入享潜。從“通道切換GPU的閑置”這張圖能夠看到,在16.67ms內(nèi)颓帝,Render的紅色部分分成5塊米碰,對(duì)應(yīng)著5個(gè)通道,由于第一個(gè)和最后一個(gè)通道對(duì)應(yīng)著全尺寸的圖片购城,所以這兩個(gè)通道處理的時(shí)間比其他3個(gè)要多一些吕座,反映在圖上也就是寬一些。5個(gè)紅色Bar中的4個(gè)橙色bar是在進(jìn)行渲染通道的切換瘪板,此時(shí)GPU處于閑置狀態(tài)吴趴。

使用shouldRasterize強(qiáng)制觸發(fā)離屏渲染:
將CALayer的shouldRasterize設(shè)置為YES,會(huì)把CALayer對(duì)應(yīng)的位圖放入緩存中侮攀。
什么情況下適合圖層?xùn)鸥窕?br> 1> 當(dāng)CALayer的內(nèi)容是靜態(tài)的锣枝,也就是CALayer內(nèi)容不會(huì)發(fā)生變化。
2> 圖層結(jié)構(gòu)比較復(fù)雜兰英。
3> 使用該圖層的地方比較多撇叁,存放進(jìn)緩存中的位圖可以多次命中。

參考文獻(xiàn)

[1]: iOS核心動(dòng)畫(huà)高級(jí)技巧
[2]: 繪制像素到屏幕上
[3]: Advanced Graphics and Animations for iOS Apps
[4]: Improving Animation Performance
[5]: 內(nèi)存惡鬼drawRect
[6]: 深刻理解移動(dòng)端優(yōu)化之離屏渲染

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末畦贸,一起剝皮案震驚了整個(gè)濱河市陨闹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌薄坏,老刑警劉巖趋厉,帶你破解...
    沈念sama閱讀 222,378評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異胶坠,居然都是意外死亡君账,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)沈善,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乡数,“玉大人,你說(shuō)我怎么就攤上這事矮瘟⊥В” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,983評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵澈侠,是天一觀的道長(zhǎng)劫侧。 經(jīng)常有香客問(wèn)我,道長(zhǎng)哨啃,這世上最難降的妖魔是什么烧栋? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,938評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮拳球,結(jié)果婚禮上审姓,老公的妹妹穿的比我還像新娘。我一直安慰自己祝峻,他們只是感情好魔吐,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布扎筒。 她就那樣靜靜地躺著,像睡著了一般酬姆。 火紅的嫁衣襯著肌膚如雪嗜桌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,549評(píng)論 1 312
  • 那天辞色,我揣著相機(jī)與錄音骨宠,去河邊找鬼。 笑死相满,一個(gè)胖子當(dāng)著我的面吹牛招刹,可吹牛的內(nèi)容都是我干的派敷。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仗嗦!你這毒婦竟也來(lái)了雪标?” 一聲冷哼從身側(cè)響起才避,我...
    開(kāi)封第一講書(shū)人閱讀 39,991評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤货裹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后躲撰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體针贬,經(jīng)...
    沈念sama閱讀 46,522評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評(píng)論 3 342
  • 正文 我和宋清朗相戀三年拢蛋,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桦他。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,742評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谆棱,死狀恐怖快压,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情垃瞧,我是刑警寧澤蔫劣,帶...
    沈念sama閱讀 36,413評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站个从,受9級(jí)特大地震影響脉幢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜嗦锐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評(píng)論 3 335
  • 文/蒙蒙 一嫌松、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧奕污,春花似錦萎羔、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,572評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)缘眶。三九已至,卻和暖如春髓废,著一層夾襖步出監(jiān)牢的瞬間磅崭,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,671評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工瓦哎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留柔逼,地道東北人蒋譬。 一個(gè)月前我還...
    沈念sama閱讀 49,159評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像愉适,于是被迫代替她去往敵國(guó)和親犯助。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評(píng)論 2 361

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

  • 繪制像素到屏幕上 answer-huang22 Mar 2014 分享文章 一個(gè)像素是如何繪制到屏幕上去的维咸?有很多...
    阿貍旅途T恤閱讀 1,640評(píng)論 0 7
  • 卷首語(yǔ) 歡迎來(lái)到 objc.io 的第三期剂买! 這一期都是關(guān)于視圖層的。當(dāng)然視圖層有很多方面癌蓖,我們需要把它們縮小到幾...
    評(píng)評(píng)分分閱讀 1,777評(píng)論 0 18
  • 有很多種framework以及很多種方法的組合可以在屏幕上渲染UI元素瞬哼,我們?cè)谶@里討論這個(gè)過(guò)程中發(fā)生的事情,希望這...
    縱橫而樂(lè)閱讀 4,497評(píng)論 4 25
  • 本系列文章的重點(diǎn)是關(guān)注在總結(jié)iOS圖形圖像的原理和性能優(yōu)化的常規(guī)解決方案租副。 事先聲明坐慰,本文絕大多數(shù)概念和內(nèi)容均來(lái)源...
    ac3閱讀 3,819評(píng)論 10 14
  • 每個(gè)人,都有一個(gè)五彩斑斕的夢(mèng)用僧。不管你有多少歲结胀,這個(gè)夢(mèng),就是我們生活的勇氣和希望责循,不管多久能夠?qū)崿F(xiàn)糟港,亦或者根本不能實(shí)...
    ERIN191閱讀 189評(píng)論 0 0