日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Objective-C 高性能的循环遍历 forin - NSEnumerator - 枚举 优化

發(fā)布時間:2023/12/15 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Objective-C 高性能的循环遍历 forin - NSEnumerator - 枚举 优化 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Cocoa編程的一個通常的任務(wù)是要去循環(huán)遍歷一個對象的集合 ?(例如,一個 NSArray, NSSet 或者是 NSDictionary). 這個看似簡單的問題有廣泛數(shù)量的解決方案,它們中的許多不乏有對性能方面問題的細微考慮.

對于速度的追求

首先,是一個免責(zé)聲明: 相比其它問題而言,一個 Objective-C 方法原始的速度是你在編程時最后才需要考慮的問題之一?– 區(qū)別就在于這個問題夠不上去同其它更加需要重點考慮的問題進行比較,比如說代碼的清晰度和可讀性.

但速度的次要性并不妨礙我們?nèi)ダ斫馑? 你應(yīng)該經(jīng)常去了解一下性能方面的考慮將如何對你正在編寫的代碼產(chǎn)生影響,一邊在極少數(shù)發(fā)生問題的情況下,你會知道如何下手.

還有,在循環(huán)的場景中,大多數(shù)時候不管是從可讀性或者是清晰度考慮,你選擇哪種技術(shù)都沒什么關(guān)系的, 所以你還不如選擇速度最快的那一種. 沒有必要選擇編碼速度比要求更慢的。

leoxu
翻譯于 2年前

1人頂

頂?翻譯的不錯哦!

考慮到這一點,就有了如下的選擇:

?

?

經(jīng)典的循環(huán)方式

?
1 2 3 for?(NSUInteger?i?=?0;?i?<?[array?count];?i++){ ??id?object?=?array[i]; ??…}

這是循環(huán)遍歷一個數(shù)組的一個簡單熟悉的方式; 從性能方面考慮它也相當?shù)牟顒? 這段代碼最大的問題就是循環(huán)每進行一次我們都會調(diào)用數(shù)組的計數(shù)方法. 數(shù)組的總數(shù)是不會改變的,因此每次都去調(diào)用一下這種做法是多余的. 像這種代碼一般C編譯器一般都會優(yōu)化掉, 但是 Objective-C 的動態(tài)語言特性意味著對這個方法的調(diào)用不會被自動優(yōu)化掉. 因此,為了提升性能,值得我們在循環(huán)開始之前,將這個總數(shù)存到一個變量中,像這樣:

?
1 2 3 NSUInteger?count?=?[array?count];for?(NSUInteger?i?=?0;?i?<?count;?i++){ ??id?object?=?array[i]; ??…}
leoxu
翻譯于 2年前

1人頂

頂?翻譯的不錯哦!

NSEnumerator

NSEnumerator 是循環(huán)遍歷集合的一種可選方式. 所有的集合都已一個或者更多個枚舉方法,每次它們被調(diào)用的時候都會返回一個NSEnumerator實體. 一個給定的 NSEnumerator 會包含一個指向集合中第一個對象的指針, 并且會有一個 nextObject 方法返回當前的對象并對指針進行增長. 你可以重復(fù)調(diào)用它直到它返回nil,這表明已經(jīng)到了集合的末尾了:

?
1 2 3 id?obj?=?nil;NSEnumerator?*enumerator?=?[array?objectEnumerator];while?((obj?=?[enumerator?nextObject]));{ ??…?????????? }

NSEnumerator 的性能可以媲美原生的for循環(huán), 但它更加實用,因為它對索引的概念進行了抽象,這意味著它應(yīng)用在結(jié)構(gòu)化數(shù)據(jù)上,比如鏈表,或者甚至是無窮序列和數(shù)據(jù)流,這些結(jié)構(gòu)中的數(shù)據(jù)條數(shù)未知或者并沒有被定義.

leoxu
翻譯于 2年前

0人頂

頂?翻譯的不錯哦!

快速枚舉

快速枚舉是在 Objective-C 2.0 中作為傳統(tǒng)的NSEnumerator的更便利(并且明顯更快速) 的替代方法而引入的. 它并沒有使得枚舉類過時因為其仍然被應(yīng)用于注入反向枚舉, 或者是當你需要對集合進行變更操作 (之后會更多地提到) 這些場景中.

快速枚舉添加了一個看起來像下面這樣子的新的枚舉方法:

?
1 2 -?(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState?*)state? ???objects:(id?*)stackbuf?count:(NSUInteger)len;

如果你正在想著“那看起來并不怎么舒服啊!”, 我不會怪你的. 但是新的方法順便帶來了一種新的循環(huán)語法, for…in 循環(huán). 這是在幕后使用了新的枚舉方法, 并且重要的是在語法和性能上都比使用傳統(tǒng)的for循環(huán)或者 NSEnumerator 方法都更省心了:

?
1 2 for?(id?object?in?array){ ??…}
leoxu
翻譯于 2年前

0人頂

頂?翻譯的不錯哦!

其它翻譯版本(1)

枚舉塊

隨著塊的誕生,Apple加入第四個基于塊語法的枚舉機制. 這無疑比快速枚舉更加的少見, 但是有一個優(yōu)勢就是對象和索引都會返回, 而其他的枚舉方法只會返回對象.

枚舉塊的另外一個關(guān)鍵特性就是可選擇型的并發(fā)枚舉 (在幾個并發(fā)的線程中枚舉對象). 這不是經(jīng)常有用,取決于你在自己的循環(huán)中具體要做些什么, 但是在你正有許多工作要做,并且你并不怎么關(guān)心枚舉順序的場景下,它在多核處理器上可能會產(chǎn)生顯著的性能提高?(現(xiàn)在所有的?Mac和iOS設(shè)備都已經(jīng)有了多核處理器).

leoxu
翻譯于 2年前

0人頂

頂?翻譯的不錯哦!

基準測試

那么這些方法疊加起來會如何呢, 性能會更加的好么? 這里有一個簡單的基準測試命令行應(yīng)用,比較了使用多種不同方法枚舉一個數(shù)據(jù)的性能.?我們已經(jīng)在 ARC 關(guān)閉的情況下運行了它,以排除任何干擾最終結(jié)果的隱藏在幕后的保留或者排除處理. 由于是運行在一個很快的 Mac 機上面, 所有這些方法運行極快以至于我們實際上不得不使用一個存有10,000,000 (一千萬) 對象的數(shù)組來測量結(jié)果. 如果你決定在一個 iPhone 進行測試, 最明智的做法是使用一個小得多的數(shù)量!

為了編譯這段代碼:

  • 把代碼保存在一個文件中,命名為?benchmark.m

  • 在終端中編譯應(yīng)用程序:
    clang -framework Foundation benchmark.m -o benchmark

  • 運行程序:?./benchmark

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #import?<Foundation/Foundation.h>?int?main(int?argc,?const?char?*?argv[]){ ??@autoreleasepool??{ ????static?const?NSUInteger?arrayItems?=?10000000; ?????NSMutableArray?*array?=?[NSMutableArray?arrayWithCapacity:arrayItems];????for?(int?i?=?0;?i?<?arrayItems;?i++)?[array?addObject:@(i)]; ????array?=?[array?copy]; ?? ????CFTimeInterval?start?=?CFAbsoluteTimeGetCurrent(); ?????//?Naive?for?loop ????for?(NSUInteger?i?=?0;?i?<?[array?count];?i++) ????{ ??????id?object?=?array[i];????}? ????CFTimeInterval?forLoop?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"For?loop:?%g",?forLoop?-?start); ?????//?Optimized?for?loop ????NSUInteger?count?=?[array?count];????for?(NSUInteger?i?=?0;?i?<??count;?i++) ????{ ??????id?object?=?array[i];????}? ????CFTimeInterval?forLoopWithCountVar?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Optimized?for?loop:?%g",?forLoopWithCountVar?-?forLoop); ?????//?NSEnumerator ????id?obj?=?nil;????NSEnumerator?*enumerator?=?[array?objectEnumerator];????while?((obj?=?[enumerator?nextObject])) ????{?????}? ????CFTimeInterval?enumeratorLoop?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Enumerator:?%g",?enumeratorLoop?-?forLoopWithCountVar); ?????//?Fast?enumeration ????for?(id?object?in?array) ????{?????}? ????CFTimeInterval?forInLoop?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"For…in?loop:?%g",?forInLoop?-?enumeratorLoop); ?????//?Block?enumeration ????[array?enumerateObjectsUsingBlock:^(id?obj,?NSUInteger?idx,?BOOL?*stop)?{?????}]; ?? ????CFTimeInterval?enumerationBlock?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Enumeration?block:?%g",?enumerationBlock?-?forInLoop); ?????//?Concurrent?enumeration ????[array?enumerateObjectsWithOptions:NSEnumerationConcurrent? ??????usingBlock:^(id?obj,?NSUInteger?idx,?BOOL?*stop)?{?????}]; ?? ????CFTimeInterval?concurrentEnumerationBlock?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Concurrent?enumeration?block:?%g",? ??????concurrentEnumerationBlock?-?enumerationBlock);??} ??return?0;}

下面展示出了結(jié)果:

?
1 2 3 4 5 6 $?For?loop:?0.119066 $?Optimized?for?loop:?0.092441 $?Enumerator:?0.123687 $?For…in?loop:?0.049296 $?Enumeration?block:?0.295039 $?Concurrent?enumeration?block:?0.199684
leoxu
翻譯于 2年前

0人頂

頂?翻譯的不錯哦!

忽略掉時間的具體長短. 我們感興趣的是它們同其它方法比較的相對大小. 如果我們按順序排列它們,快的放前面,我會得到了下面的結(jié)果:

  • For…in循環(huán)?– 最快.

  • 對for循環(huán)的優(yōu)化?– 比 for…in 慢兩倍.

  • 沒有優(yōu)化的for循環(huán) – 比?for…in 慢2.5倍.

  • Enumerator – 大約同沒有優(yōu)化的循環(huán)相同.

  • 并發(fā)的枚舉塊?– 比 for…in 大約慢6倍.

  • 枚舉塊 – 比?for…in 幾乎慢6倍.

  • For…in 是勝出者. 顯然他們將其稱為快速枚舉是有原因的! 并發(fā)枚舉看起來是比單線程的快一點點, 但是你沒必要對其做更多的解讀: 我們這里是在枚舉一個非常非常大型的對象數(shù)組,而對于小一些的數(shù)據(jù)并發(fā)執(zhí)行的開銷遠多于其帶來的好處.

    并發(fā)執(zhí)行的主要是在當你的循環(huán)需要大量的執(zhí)行時間時有優(yōu)勢. 如果你在自己的循環(huán)中有許多東西要運行,那就考慮試下并行枚舉,在你不關(guān)心枚舉順序的前提下 (但是請用行動的去權(quán)衡一下它是否變得更快樂,不要空手去揣度).

    leoxu
    翻譯于 2年前

    0人頂

    頂?翻譯的不錯哦!

    其它集合類型Other Collection Types

    那么其它的結(jié)合類型怎么樣呢, 比如 NSSet 和 NSDictionary? NSSet 是無序的, 因此沒有按索引去取對象的概念.我們也可以進行一下基準測試:

    ?
    1 2 3 4 $?Enumerator:?0.421863 $?For…in?loop:?0.095401 $?Enumeration?block:?0.302784 $?Concurrent?enumeration?block:?0.390825

    ?

    結(jié)果同?NSArray 一致; for…in 再一次勝出了. ?NSDictionary怎么樣了? NSDictionary 有一點不同因為我們同時又一個鍵和值對象需要迭代. 在一個字典中單獨迭代鍵或者值是可以的, 但典型的情況下我們兩者都需要. 這里我們有一段適配于操作NSDictionary的基準測試代碼:

    ?
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #import?<Foundation/Foundation.h>?int?main(int?argc,?const?char?*?argv[]){ ??@autoreleasepool??{ ????static?const?NSUInteger?dictItems?=?10000; ?????NSMutableDictionary?*dictionary?=? ??????[NSMutableDictionary?dictionaryWithCapacity:dictItems];????for?(int?i?=?0;?i?<?dictItems;?i++)?dictionary[@(i)]?=?@(i); ????dictionary?=?[dictionary?copy]; ?? ????CFTimeInterval?start?=?CFAbsoluteTimeGetCurrent(); ?????//?Naive?for?loop ????for?(NSUInteger?i?=?0;?i?<?[dictionary?count];?i++) ????{ ??????id?key?=?[dictionary?allKeys][i];??????id?object?=?dictionary[key];????}? ????CFTimeInterval?forLoop?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"For?loop:?%g",?forLoop?-?start); ?????//?Optimized?for?loop ????NSUInteger?count?=?[dictionary?count];????NSArray?*keys?=?[dictionary?allKeys];????for?(NSUInteger?i?=?0;?i?<??count;?i++) ????{ ??????id?key?=?keys[i];??????id?object?=?dictionary[key];????}? ????CFTimeInterval?forLoopWithCountVar?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Optimized?for?loop:?%g",?forLoopWithCountVar?-?forLoop); ?????//?NSEnumerator ????id?key?=?nil;????NSEnumerator?*enumerator?=?[dictionary?keyEnumerator];????while?((key?=?[enumerator?nextObject])) ????{ ??????id?object?=?dictionary[key];????}? ????CFTimeInterval?enumeratorLoop?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Enumerator:?%g",?enumeratorLoop?-?forLoopWithCountVar); ?????//?Fast?enumeration ????for?(id?key?in?dictionary) ????{ ??????id?object?=?dictionary[key];????}? ????CFTimeInterval?forInLoop?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"For…in?loop:?%g",?forInLoop?-?enumeratorLoop); ?????//?Block?enumeration ????[dictionary?enumerateKeysAndObjectsUsingBlock:^(id?key,?id?obj,?BOOL?*stop)?{?????}]; ?? ????CFTimeInterval?enumerationBlock?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Enumeration?block:?%g",?enumerationBlock?-?forInLoop); ?????//?Concurrent?enumeration ????[dictionary?enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent? ??????usingBlock:^(id?key,?id?obj,?BOOL?*stop)?{?????}]; ?? ????CFTimeInterval?concurrentEnumerationBlock?=?CFAbsoluteTimeGetCurrent(); ????NSLog(@"Concurrent?enumeration?block:?%g",? ??????concurrentEnumerationBlock?-?enumerationBlock);??} ??return?0;}
    leoxu
    翻譯于 2年前

    0人頂

    頂?翻譯的不錯哦!

    NSDictionary 填充起來比?NSArray 或者 NSSet 慢得多, 因此我們把數(shù)據(jù)條數(shù)減少到了10,000 (一萬) 以避免機器鎖住. 因而你應(yīng)該忽略結(jié)果怎么會比那些?NSArray 低那么多,因為我們使用的是更少對象的?1000 次循環(huán):

    ?
    1 2 3 4 5 6 $?For?loop:?2.25899 $?Optimized?for?loop:?0.00273103 $?Enumerator:?0.00496799 $?For…in?loop:?0.001041 $?Enumeration?block:?0.000607967 $?Concurrent?enumeration?block:?0.000748038

    ?

    沒有優(yōu)化過的循環(huán)再這里慢得很壯觀,因為每一次我們都復(fù)制了鍵數(shù)組. 通過把鍵數(shù)組和總數(shù)存到變量中,我們獲得了更快的速度. 查找對象的消耗現(xiàn)在主宰了其它的因素,因此使用一個for循環(huán), NSEnumerator 或者for…in 差別很小. 但是對于枚舉塊方法而言,它在一個方法中把鍵和值都返回了,所以現(xiàn)在變成了最快的選擇。

    leoxu
    翻譯于 2年前

    0人頂

    頂?翻譯的不錯哦!

    反轉(zhuǎn)齒輪

    基于我們所見,如果所有其它的因素都一樣的話,在循環(huán)遍歷數(shù)組時你應(yīng)該嘗試去使用for...in循環(huán), 而遍歷字典時,則應(yīng)該選擇枚舉塊. 也有一些場景下這樣的做法并不可能行得通,比如我們需要回頭來進行枚舉,或者當我們在遍歷時想要變更集合的情況.

    為了回過頭來枚舉一個數(shù)據(jù),我們可以調(diào)用reverseObjectEnumerator方法來獲得一個NSEnumerator 以從尾至頭遍歷數(shù)組. NSEnumerator, 就像是 NSArray 它自己, 支持快速的枚舉協(xié)議. 那就意味著我們?nèi)匀豢梢栽谶@種方式下使用 for…in, 而無速度和簡潔方面的損失:

    ?
    1 2 3 ??for?(id?object?in?[array?reverseObjectEnumerator])? ??{ ????…??}

    (除非你異想天開, NSSet 或者 NSDictionary 是沒有等效的方法的, 而反向枚舉一個?NSSet 或者NSDictionary無論如何都沒啥意義, 因為鍵是無序的.)

    如果你想使用枚舉塊的話, NSEnumerationReverse你可以試試, 像這樣:

    ?
    1 2 ??[array?enumerateObjectsWithOptions:NSEnumerationReverse?usingBlock:^(id?obj,?NSUInteger?idx,?BOOL?*stop)?{ ????…??}];
    leoxu
    翻譯于 2年前

    0人頂

    頂?翻譯的不錯哦!

    變更Mutation

    應(yīng)用同樣的循環(huán)技術(shù)到變更中的集合上是可能的; 其性能也大致相同. 然而當你嘗試在循環(huán)數(shù)組或者字典的時候修改它們,你可能經(jīng)常會面臨這樣的異常:

    '***?Collection?XYZ?was?mutated?while?being?enumerated.'

    ?

    就像我們優(yōu)化了的for循環(huán), 所有這些循環(huán)技術(shù)的性能取決于事先把數(shù)據(jù)總數(shù)存下來,這意味著如果你開始在循環(huán)中間加入或者去掉一個數(shù)據(jù)時,這個數(shù)據(jù)就不正確了. 但是在循環(huán)進行中加入,替換或者移除一條數(shù)據(jù)時經(jīng)常想要做的事情. 那么什么才是這個問題的解決之道呢?

    我們經(jīng)典的for循環(huán)可以工作得很好,因為它不依賴于駐留的總數(shù)常量; 我們只需要記得,如果我們添加或者移除了一條數(shù)據(jù),就要增加或者減小索引. 但我們已經(jīng)了解到for循環(huán)并不是一種速度快的解決方案. 我們優(yōu)化過的for循環(huán)則是一個合理的選擇, 只要我們記得按需遞增或者遞減技術(shù)變量,還有索引.

    leoxu
    翻譯于 2年前

    0人頂

    頂?翻譯的不錯哦!

    我們?nèi)匀豢梢允褂胒or…in, 但前提是我們首先創(chuàng)建了一個數(shù)組的拷貝. 這會起作用的,例如:

    ??for?(id?object?in?[array?copy])?{//?Do?something?that?modifies?the?array,?e.g.?[array?removeObject:object];}

    如果我們對不同的技術(shù)進行基準測試(必要時把復(fù)制數(shù)組的開銷算在內(nèi),以便我們可以對原來數(shù)組內(nèi)的數(shù)據(jù)進行變更), 我們發(fā)現(xiàn)復(fù)制抵消了 for…in 循環(huán)之前所擁有的好處:

    $?For?loop:?0.111422 $?Optimized?for?loop:?0.08967 $?Enumerator:?0.313182 $?For…in?loop:?0.203722 $?Enumeration?block:?0.436741 $?Concurrent?enumeration?block:?0.388509

    ?

    在我們遍歷一個數(shù)組時修改這個數(shù)組最快的計數(shù),似乎是需要使用一個優(yōu)化了的for循環(huán)的.

    對于一個?NSDictionary, 我們不需要為了使用NSEnumerator 或者快速枚舉而復(fù)制整個字典; 我們可以只去使用allKeys方法獲取到所有鍵的一個副本. 這都將能很好的運作起來:

    ??//?NSEnumeratorid?key?=?nil;??NSEnumerator?*enumerator?=?[[items?allKeys]?objectEnumerator];??while?((key?=?[enumerator?nextObject])){id?object?=?items[key];????//?Do?something?that?modifies?the?value,?e.g.?dictionary[key]?=?newObject;}???//?Fast?enumerationfor?(id?key?in?[dictionary?allkeys])?{id?object?=?items[key];????//?Do?something?that?modifies?the?value,?e.g.?dictionary[key]?=?newObject;}
    leoxu
    翻譯于 2年前

    0人頂

    頂?翻譯的不錯哦!

    然而同樣的技術(shù)在使用enumerateKeysAndObjectsUsingBlock方法時并不能起作用. 如果我們循環(huán)遍歷一個字典進行基準測試, 按照需要對鍵或者對字典整體創(chuàng)建備份,我們得到了下面的結(jié)果:

    $?For?loop:?2.24597 $?Optimized?for?loop:?0.00282001 $?Enumerator:?0.00508499 $?For…in?loop:?0.000990987 $?Enumeration?block:?0.00144804 $?Concurrent?enumeration?block:?0.00166804

    ?

    這里我們可以看到 for…in 循環(huán)是最快的一個. 那是因為在for...in循環(huán)中根據(jù)鍵取對象的開銷現(xiàn)在已經(jīng)被在調(diào)用枚舉塊方法之前復(fù)制字典的開銷蓋過去了.

    leoxu
    翻譯于 2年前

    0人頂

    頂?翻譯的不錯哦!

    當枚舉一個NSArray的時候:

    • 使用 for (id object in array) 如果是順序枚舉

    • 使用 for (id object in?[array reverseObjectEnumerator]) 如果是倒序枚舉

    • 使用 for (NSInteger i = 0; i < count; i++) 如果你需要知道它的索引值,或者需要改變數(shù)組

    • 嘗試?[array enumerateObjectsWithOptions:usingBlock:] 如果你的代碼受益于并行執(zhí)行

    當枚舉一個NSSet的時候:

    • 使用??for (id object in set) 大多數(shù)時候

    • 使用?for (id object in [set copy]) 如果你需要修改集合(但是會很慢)

    • 嘗試?[array enumerateObjectsWithOptions:usingBlock:] 如果你的代碼受益于并行執(zhí)行

    當枚舉一個NSDictionary的時候:

    • 使用??for (id object in set) 大多數(shù)時候

    • 使用?for (id object in [set copy]) 如果你需要修改詞典

    • 嘗試?[array enumerateObjectsWithOptions:usingBlock:] 如果你的代碼受益于并行執(zhí)行

    這些方法可能不是最快的,但他們都是非常清晰易讀的。所以請記住,有時是在不寫干凈的代碼,和快速的代碼之間做出選擇,你會發(fā)現(xiàn),你可以在兩個世界得到最好的。

    總結(jié)

    以上是生活随笔為你收集整理的Objective-C 高性能的循环遍历 forin - NSEnumerator - 枚举 优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。