編寫高質(zhì)量代碼的52個(gè)有效方法(六)—塊與大中樞派發(fā)

outstanding.png

當(dāng)前多線程編程的核心就是“塊”(block)與“大中樞派發(fā)”(Grand Central Dispatch,GCD)。

37.理解“塊”這一概念

1.塊的基礎(chǔ)知識

塊與函數(shù)類似,只不過是直接定義在另一個(gè)函數(shù)里的,和定義它的那個(gè)函數(shù)共享同一個(gè)范圍內(nèi)的東西洗做。塊用“^”符號來表示,后面跟著一對花括號,括號里面是塊的實(shí)現(xiàn)代碼义郑。

^{
    //Block implementation
}

//塊類型的語法結(jié)構(gòu)及事例

return_type (^block_name)(parameters)

//使用
block_name(parameters);

eg:

int (^addBlock)(int a, int b) = ^(int a, int b){
    return a + b;
}

//使用
int result = addBlock(3,5);

塊的強(qiáng)大之處在于:在聲明它的范圍里,所有變量都可以為其所捕獲丈钙。這就是說非驮,塊所在的范圍里的全部變量,在塊里依然可用雏赦。默認(rèn)情況下劫笙,為塊所捕獲的變量,是不可以在塊里修改的喉誊,聲明變量的時(shí)候可以加上__block修飾符邀摆,這樣就可以在塊內(nèi)修改了。對于實(shí)例變量伍茄,塊總是能夠修改的栋盹,所以對于要修改的實(shí)例變量則無需加__block.

塊的保留環(huán):塊里面使用了實(shí)例變量或self,self也是個(gè)對象敷矫,因而塊在捕獲它時(shí)也會將其保留例获。如果self所指代的那個(gè)對象同時(shí)也保留了塊汉额,那么這種情況通常就會導(dǎo)致保留環(huán)。

2.全局塊榨汤、棧塊及堆塊

定義塊的時(shí)候蠕搜,其所占的內(nèi)存區(qū)域是分配在棧中的。這就是說,塊只在定義它的那個(gè)范圍內(nèi)有效换况。

void (^block)();

if(/* some condition */){
    block = ^{
        NSLog(@"Block A");
    }
}else{
    block = ^{
        NSLog(@"Block B");
    }
}

block();

定義在if及else語句中的兩個(gè)塊都分配在棧內(nèi)存中你画。編譯器會給每個(gè)塊分配好棧內(nèi)存,然而等離開了相應(yīng)的范圍之后虫埂,編譯器有可能把分配給塊的內(nèi)存覆寫掉。于是圃验,這兩個(gè)塊只能保證在對于的if或else語句范圍內(nèi)有效掉伏。這樣寫出來的代碼可以編譯,但是運(yùn)行起來時(shí)而正確澳窑,時(shí)而錯誤斧散。若編譯器未覆寫待執(zhí)行的塊,則程序照常運(yùn)行摊聋,若覆寫鸡捐,則程序崩潰。

為解決此問題栗精,可給塊對象發(fā)送copy消息來拷貝之闯参。這樣的話,就可以把塊從棧復(fù)制到堆了悲立÷拐拷貝后的塊,可以在定義它的那個(gè)范圍之外使用薪夕。而且脚草,一旦復(fù)制到堆上,塊就成了帶引用計(jì)數(shù)的對象了原献。

//全局塊
void (^blocks)(void) = ^{
//    self.propert = @"string";//會報(bào)錯馏慨,不會捕捉任何狀態(tài)
};

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

}

除了“棧塊”和“堆塊”之外,還有一類塊叫做“全局塊”(global block)姑隅。這種塊不會捕捉任何狀態(tài)(比如外圍的變量等)写隶,運(yùn)行時(shí)也無需有狀態(tài)來參與。塊所使用的整個(gè)內(nèi)存區(qū)域讲仰,在編譯期已經(jīng)完全確定了慕趴,因此,全局塊可以聲明在全局內(nèi)存里,而不需要在每次用到的時(shí)候于棧中創(chuàng)建冕房。另外躏啰,全局塊的拷貝操作是空操作,因?yàn)槿謮K決不可能為系統(tǒng)所回收耙册。

要點(diǎn):

  1. 塊是C给僵、C++、Objective-C中的詞法閉包详拙。
  2. 塊可接受參數(shù)帝际,也可返回值
  3. 塊可以分配在棧或堆上溪厘,也可以是全局的胡本。分配在棧上的塊可拷貝到堆里,這樣的話畸悬,就和標(biāo)準(zhǔn)的Objective-C對象一樣,具備引用計(jì)數(shù)了珊佣。

38.為常用的塊類型創(chuàng)建typedef

與其他類型的變量不同蹋宦,在定義塊變量時(shí),要把變量名放在類型之中咒锻,而不是放在右側(cè)冷冗。鑒于此,我們應(yīng)該為常用的塊類型起個(gè)別名惑艇。為了隱藏復(fù)雜的塊類型蒿辙,需要用到C語言中名為“類型定義”(type definition)的特性。typedif關(guān)鍵字用于給類型起個(gè)易讀的別名滨巴。

typedef int(^BlockName)(BOOL flag, int value);

BlockName block = ^(BOOL flag, int value){
    // block implementation
}

要點(diǎn):

  1. 以typedef重新定義塊類型思灌,可令塊變量用起來更加簡單
  2. 定義新類型時(shí)應(yīng)遵循現(xiàn)有的命名習(xí)慣,勿使用其名稱與別的類型相沖突
  3. 不妨為同一個(gè)塊簽名定義多個(gè)類型別名恭取。如果要重構(gòu)的代碼使用了塊類型的某個(gè)別名泰偿,那么只需要修改相應(yīng)typedef中的塊簽名即可,無需改動其他typedef蜈垮。

39.用handler塊降低代碼分散程度

設(shè)計(jì)API時(shí)耗跛,對于回調(diào)的選擇有多種,選用合適的回調(diào)方式能夠讓我們的代碼更加清晰整潔攒发。

要點(diǎn):

  1. 在創(chuàng)建對象時(shí)调塌,可以使用內(nèi)聯(lián)的handler塊將相關(guān)業(yè)務(wù)邏輯一并聲明
  2. 在有多個(gè)實(shí)例需要監(jiān)控時(shí),如果采用委托模式惠猿,那么經(jīng)常需要根據(jù)傳入的對象來切換羔砾,而若改用handler塊來實(shí)現(xiàn),則可直接將塊與相關(guān)對象放在一起
  3. 設(shè)計(jì)API時(shí)如果用到了handler塊,那么可以增加一個(gè)參數(shù)蜒茄,使調(diào)用者可通過此參數(shù)來決定應(yīng)該把塊安排在哪個(gè)隊(duì)列上執(zhí)行唉擂。

40.用塊引用其所屬對象時(shí)不要出現(xiàn)保留環(huán)

要點(diǎn):

  1. 如果塊所捕獲的對象直接或間接地保留了塊本身,那么就得當(dāng)心保留環(huán)問題
  2. 一定要找個(gè)適當(dāng)?shù)臅r(shí)機(jī)解除保留環(huán)檀葛,而不能把責(zé)任推給API的調(diào)用者玩祟。

41.多用派發(fā)隊(duì)列,少用同步鎖

在Objective-C中屿聋,如果有多個(gè)線程要執(zhí)行同一份代碼空扎,那么有時(shí)可能會出現(xiàn)問題。這種情況下润讥,通常要使用鎖來實(shí)現(xiàn)某種同步機(jī)制转锈,在GCD出現(xiàn)之前,有兩種辦法楚殿,第一種是采用內(nèi)置的“同步塊”(synchronization block)撮慨;第二種是直接使用NSLock對象;

//同步塊(synchronization block)

- (void)synchronizedMehtod{
    @synchronized(self){
        // safe 安全的執(zhí)行代碼
    }
}

這種寫法會根據(jù)給定的對象脆粥,自動創(chuàng)建一個(gè)鎖砌溺,并等待塊中的代碼執(zhí)行完畢。執(zhí)行到這段代碼結(jié)尾處变隔,鎖就釋放了规伐。這么寫通常沒錯,因?yàn)樗梢员WC每個(gè)對象實(shí)例都能不受干擾地運(yùn)行其synchronizationMehtod方法匣缘。然而猖闪,濫用@synchronized(self)則會降低代碼效率,因?yàn)楣灿猛粋€(gè)鎖的那些同步塊肌厨,都必須按照順序執(zhí)行培慌。若是在self對象上頻繁加鎖,那么程序可能要等另一段與此無關(guān)的代碼執(zhí)行完畢夏哭,才能繼續(xù)執(zhí)行當(dāng)前代碼检柬,這樣做其實(shí)并沒有必要。

//NSLock對象

_lock = [[NSLock alloc] init];

- (void)synchronizedMethod{

    [_lock lock];
    
    //safe code
    
    [_lock unlock];
}

也可以使用NSRecursiveLock這種“遞歸鎖”(recursive lock),線程能夠多次持有該鎖竖配,而不會出現(xiàn)死鎖(deadlock)現(xiàn)象何址。這兩種方法都很好,不過也有其缺陷进胯。比方說用爪,在極端情況下,同步塊會導(dǎo)致死鎖胁镐,另外偎血,效率也不見得很高诸衔,而如果直接使用鎖對象的話,一旦遇到死鎖颇玷,就會非常麻煩笨农。

有種簡單而高效的辦法可以代替同步塊或鎖對象,那就是使用“串行同步隊(duì)列”帖渠。將讀取操作及寫入操作都安排在同一個(gè)隊(duì)列里谒亦,即可保證數(shù)據(jù)同步。

_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue",NULL);

- (NSString *)someString{
    __block NSString *localString;
    dispatch_sync(_syncQueue,^{
        localString = _someString;
    });
    return localString;
}

- (void)setSomeString:(NSString *)someString{
    dispatch_sync(_syncQueue,^{
        _someString = someString;
    });
}

此模式的思路是:把設(shè)置操作與獲取操作都安排在序列化的隊(duì)列里執(zhí)行空郊,這樣的話份招,所有針對屬性的訪問操作就都同步了。為了shi塊代碼能夠設(shè)置局部變量狞甚,獲取方法中用到了__block語法锁摔,若是拋開這一點(diǎn),那么這種寫法要比前面那些更為整潔哼审。全部加鎖任務(wù)都在GCD中處理谐腰,而GCD是在相當(dāng)深的底層來實(shí)現(xiàn)的,于是能夠做許多優(yōu)化涩盾。因此怔蚌,開發(fā)者無需擔(dān)心那些事,只要專心把訪問方法寫好就行旁赊。

要點(diǎn):

  1. 派發(fā)隊(duì)列可用來表述同步語義(synchronization semantic),這種做法要比使用@synchronized塊或NSLock對象更簡單
  2. 將同步與異步派發(fā)結(jié)合起來椅野,可以實(shí)現(xiàn)與普通加鎖機(jī)制一樣的同步行為终畅,而這么做卻不會阻塞執(zhí)行異步派發(fā)的形成
  3. 使用同步隊(duì)列及柵欄塊,可以令同步行為更加高效

42.多用GCD,少用performSelector系列方法

SEL selector = @selector(test);
[self performSelector:selector];

報(bào)警告:PerformSelector may cause a leak because its selector is unknown

原因在于:編譯器并不知道將要調(diào)用的選擇子是什么竟闪,因此离福,也就不了解其方法簽名及返回值,甚至連是否有返回值都不清楚炼蛤。而且妖爷,由于編譯器不知道方法名,所以就沒辦法運(yùn)用ARC的內(nèi)存管理規(guī)則來判定返回值是不是應(yīng)該釋放理朋。鑒于此絮识,ARC采用了比較謹(jǐn)慎的做法,就是不添加釋放操作嗽上。然而這么做可能導(dǎo)致內(nèi)存泄漏次舌,因?yàn)榉椒ㄔ诜祷貙ο髸r(shí)可能已經(jīng)將其保留了。

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

傳入的參數(shù)都是id類型兽愤,所以傳入的參數(shù)必須是對象才行彼念,基本數(shù)據(jù)類型不行挪圾;再者,返回值也是id逐沙。還有一個(gè)問題就是哲思,多個(gè)參數(shù)的傳遞,我們可能需要使用字典等集合來進(jìn)行封裝再進(jìn)行傳遞吩案。

如果改為其他替代方案棚赔,那就不受這些限制了。最主要的替代方案就是使用塊务热。

//using performSelector
    [self performSelector:@selector(doSomeThing) withObject:nil afterDelay:5.0];
    
    //using GCD
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^{
        [self doSomeThing];
    });
//using performSelector
    [self performSelectorOnMainThread:@selector(doSomeThing) withObject:nil waitUntilDone:NO];
    
    //using GCD
    //if waitUntilDone is YES,then dispatch_sync
    dispatch_async(dispatch_get_main_queue(), ^{
        [self doSomeThing];
    });

要點(diǎn):

  1. performSelector系列方法在內(nèi)存管理方法容易有疏失忆嗜。它無法確定將要執(zhí)行的選擇子具體是什么,因而ARC編譯器也就無法插入適當(dāng)?shù)膬?nèi)存管理方法崎岂。
  2. performSelector系列方法所能處理的選擇子太多局限了捆毫,選擇子的返回值類型及發(fā)送給方法的參數(shù)個(gè)數(shù)都受到限制
  3. 如果想把任務(wù)放另一個(gè)線程上執(zhí)行,那么最好不要用performSelector系列方法冲甘,而是應(yīng)該把任務(wù)封裝到塊里绩卤,然后調(diào)用大中樞派發(fā)機(jī)制的相關(guān)方法來實(shí)現(xiàn)。

43.掌握GCD及操作隊(duì)列的使用時(shí)機(jī)

出了GCD之外江醇,還有一種技術(shù)叫做NSOperationQueue,它雖然與GCD不同濒憋,但是卻與之相關(guān),開發(fā)者可以把操作以NSOperation子類的形式放在隊(duì)列中陶夜,而這些操作也能并發(fā)執(zhí)行凛驮。區(qū)別:GCD是純C的API,而操作隊(duì)列則是Objective-C的對象条辟。在GCD中黔夭,任務(wù)用塊來表示。用NSOperationQueue類的“addOperationWithBlock:”方法搭配NSBlockOperation類來使用操作隊(duì)列羽嫡,其語法與GCD方式類似本姥。使用NSOperation及NSOperationQueue的好處如下:

  1. 可以取消某個(gè)操作。
  2. 指定操作間的依賴關(guān)系杭棵。
  3. 通過鍵值觀測機(jī)制監(jiān)控NSOperation對象的屬性婚惫。
  4. 指定操作的優(yōu)先級。
  5. 重用NSOperation對象

NSNotificationCeter使用的就是操作隊(duì)列而非派發(fā)隊(duì)列


- (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block


[[NSNotificationCenter defaultCenter] addObserverForName:(nullable NSNotificationName) object:(nullable id) queue:(nullable NSOperationQueue *) usingBlock:^(NSNotification * _Nonnull note) {
        
    }];

要點(diǎn):

  1. 在解決多線程與任務(wù)管理問題時(shí)魂爪,派發(fā)隊(duì)列并非唯一方案先舷。
  2. 操作隊(duì)列提供了一套高層的Objective-C API,能實(shí)現(xiàn)純GCD所具備的絕大部分功能甫窟,而且還能完成一些更為復(fù)雜的操作密浑,那些操作若改用GCD來實(shí)現(xiàn),則需另外編寫代碼

44.通過Dispatch Group機(jī)制粗井,根據(jù)系統(tǒng)資源狀況來執(zhí)行任務(wù)

GCD常見方法

//創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
    
    
//任務(wù)編組
//方式一:把待執(zhí)行的任務(wù)塊歸屬某個(gè)組
void dispatch_group_async(dispatch_group_t group,
                         dispatch_queue_t queue,
                         dispatch_block_t block);
//方式二:進(jìn)組 與 出組 成對出現(xiàn)
void dispatch_group_enter(dispatch_group_t group);
void dispatch_group_leave(dispatch_group_t group);
    
    
//等待dispatch_group執(zhí)行完畢
//arg0:等待的隊(duì)列組 arg1:等待時(shí)間 DISPATCH_TIME_FOREVER表示一直等著dispatch_group執(zhí)行完
//返回類型long,如果執(zhí)行g(shù)roup所需的時(shí)間小于timeout尔破,則返回0街图,否則返回非0值
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
    
//通知隊(duì)列組執(zhí)行完后在指定的隊(duì)列進(jìn)行回調(diào) 與上面的方法相比,在非主隊(duì)列中不會阻塞
void dispatch_group_notify(dispatch_group_t group,
                          dispatch_queue_t queue,
                          dispatch_block_t block);
    
    
//GCD遍歷集合 該方法會持續(xù)阻塞懒构,從0開始餐济,直至iterations - 1
 void dispatch_apply(size_t iterations, dispatch_queue_t queue,DISPATCH_NOESCAPE void (^block)(size_t));

要點(diǎn):

  1. 一系列任務(wù)可歸入一個(gè)dispatch group中。開發(fā)者可以在這組執(zhí)行完畢時(shí)獲得通知胆剧。
  2. 通過dispatch group,可以在并發(fā)式派發(fā)隊(duì)列中同時(shí)執(zhí)行多項(xiàng)任務(wù)絮姆。此時(shí)GCD會根據(jù)系統(tǒng)資源來調(diào)度這些并發(fā)執(zhí)行的任務(wù)。開發(fā)者若自己來實(shí)現(xiàn)此功能秩霍,則需要編寫大量代碼篙悯。

45.使用dispatch_once來執(zhí)行只需運(yùn)行一次的線程安全代碼

//單例中的dispatch_once使用

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
        
    });

要點(diǎn):

  1. 經(jīng)常需要編寫“只需要執(zhí)行一次的線程安全代碼”。通過GCD所提供的dispatch_once函數(shù)铃绒,很容易就能實(shí)現(xiàn)此功能鸽照。
  2. 標(biāo)記應(yīng)該聲明在static或global作用域中,這樣的話颠悬,在把只需執(zhí)行一次的塊傳給dispatch_once函數(shù)時(shí)矮燎,傳進(jìn)去的標(biāo)記也是相同的。

46.不要使用dispatch_get_current_queue

要點(diǎn):

  1. dispatch_get_current_queue函數(shù)的行為常常與開發(fā)者所預(yù)期的不同赔癌。此函數(shù)已經(jīng)廢棄诞外,只應(yīng)做調(diào)試使用
  2. 由于派發(fā)隊(duì)列是按層級來組織的,所以無法單用某個(gè)隊(duì)列對象來描述“當(dāng)前隊(duì)列”這一概念
  3. dispatch_get_current_queue函數(shù)用于解決由不可重入的代碼所引發(fā)的死鎖灾票,然而能用此函數(shù)解決的問題峡谊,通常也能改用“隊(duì)列特定數(shù)據(jù)”來解決



PDF格式的資料來自iOS開發(fā)交流群、感覺作者的貢獻(xiàn)刊苍,對于知識的系統(tǒng)歸納總結(jié)很有幫助靖苇。
編寫高質(zhì)量代碼的52個(gè)有效方法
編寫高質(zhì)量代碼的52個(gè)有效方法(一)—熟悉OC
編寫高質(zhì)量代碼的52個(gè)有效方法(二)—對象、消息班缰、運(yùn)行期
編寫高質(zhì)量代碼的52個(gè)有效方法(三)—接口與API設(shè)計(jì)
編寫高質(zhì)量代碼的52個(gè)有效方法(四)—協(xié)議與分類
編寫高質(zhì)量代碼的52個(gè)有效方法(五)—內(nèi)存管理
編寫高質(zhì)量代碼的52個(gè)有效方法(六)—塊與大中樞派發(fā)
編寫高質(zhì)量代碼的52個(gè)有效方法(七)---系統(tǒng)框架

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市悼枢,隨后出現(xiàn)的幾起案子埠忘,更是在濱河造成了極大的恐慌,老刑警劉巖馒索,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件莹妒,死亡現(xiàn)場離奇詭異,居然都是意外死亡绰上,警方通過查閱死者的電腦和手機(jī)旨怠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜈块,“玉大人鉴腻,你說我怎么就攤上這事迷扇。” “怎么了爽哎?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵蜓席,是天一觀的道長。 經(jīng)常有香客問我课锌,道長厨内,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任渺贤,我火速辦了婚禮雏胃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘志鞍。我一直安慰自己瞭亮,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布述雾。 她就那樣靜靜地躺著街州,像睡著了一般。 火紅的嫁衣襯著肌膚如雪玻孟。 梳的紋絲不亂的頭發(fā)上唆缴,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音黍翎,去河邊找鬼面徽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛匣掸,可吹牛的內(nèi)容都是我干的趟紊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼碰酝,長吁一口氣:“原來是場噩夢啊……” “哼霎匈!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起送爸,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤铛嘱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后袭厂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體墨吓,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年纹磺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了帖烘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡橄杨,死狀恐怖秘症,靈堂內(nèi)的尸體忽然破棺而出照卦,到底是詐尸還是另有隱情,我是刑警寧澤历极,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布窄瘟,位于F島的核電站,受9級特大地震影響趟卸,放射性物質(zhì)發(fā)生泄漏蹄葱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一锄列、第九天 我趴在偏房一處隱蔽的房頂上張望图云。 院中可真熱鬧,春花似錦邻邮、人聲如沸竣况。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丹泉。三九已至,卻和暖如春鸭蛙,著一層夾襖步出監(jiān)牢的瞬間摹恨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工娶视, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留晒哄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓肪获,卻偏偏與公主長得像寝凌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子孝赫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349

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