ReactiveCocoa學(xué)習(xí)筆記六-RACDisposable源碼分析

RACDisposable算是三個(gè)核心組件里面最簡(jiǎn)單的了, 除了自身外, 還有4個(gè)子類:
RACKVOTrampoline, RACScopedDisposable 以及在Signal中已經(jīng)介紹過(guò)的RACCompoundDisposable和RACSerialDisposable. 這一部分的代碼還是比較好理解的, 就是有很多底層一些的代碼, 所以需要多查查文檔.

RACDisposable

頭文件對(duì)這個(gè)類的描述就是對(duì)訂閱關(guān)系的取消和相關(guān)資源的清理, 我們就看看它是怎么清理的.

// RACDisposable.h
// 標(biāo)記是否已經(jīng)清除的狀態(tài)
@property (atomic, assign, getter = isDisposed, readonly) BOOL disposed;
+ (instancetype)disposableWithBlock:(void (^)(void))block;
- (void)dispose;
- (RACScopedDisposable *)asScopedDisposable; // 轉(zhuǎn)換為RACScopedDisposable

屬性和方法的意義還是比較明顯的, 然后看實(shí)現(xiàn)文件也比較"言簡(jiǎn)意賅", 大致意思是存儲(chǔ)起來(lái)dispose需要的block, 在調(diào)用時(shí)執(zhí)行這個(gè)block(如果block不為空)[注意, 如果不是所有的RACDisposable在dealloc時(shí)都會(huì)自動(dòng)調(diào)用dispose喲]. 但是里面的實(shí)現(xiàn)用到了很多C語(yǔ)言和一些非ARC的東西, 所以看起來(lái)比較高深.

先看生命周期函數(shù):

// 一個(gè)實(shí)際上沒(méi)有什么可清除的disposable, 基本上可以看做一個(gè)標(biāo)記是否已dispose的值
- (id)init {
    self = [super init];
    if (self == nil) return nil;

    // 需要注意: 如果沒(méi)有block, _disposeBlock會(huì)指向自己, 所以后續(xù)操作都會(huì)進(jìn)行判斷是否等于自己, 因此_disposeBlock的類型也是void *. 之所以不是id類型, 是因?yàn)闀?huì)引用自己, 這樣就循環(huán)引用了. 
    _disposeBlock = (__bridge void *)self;
    // OSMemoryBarrier是確保前面的代碼執(zhí)行完了之后再執(zhí)行后面的, 
    // 這個(gè)在多核CPU中可以保證安全
    OSMemoryBarrier();

    return self;
}

- (id)initWithBlock:(void (^)(void))block {
    NSCParameterAssert(block != nil);

    self = [super init];
    if (self == nil) return nil;
    
    // 這里其實(shí)不是真正的retain, 只是一個(gè)橋接, 把Objc類型轉(zhuǎn)換為CoreFoundation類型(也就是C類型), 因?yàn)開(kāi)disposeBlock是void *類型
    _disposeBlock = (void *)CFBridgingRetain([block copy]); 
    OSMemoryBarrier();

    return self;
}

- (void)dealloc {
    // 為空和等于自身都不需要額外釋放或者設(shè)置NULL
    if (_disposeBlock == NULL || _disposeBlock == (__bridge void *)self) return;

    CFRelease(_disposeBlock);
    _disposeBlock = NULL;
}

核心的dispose也是一個(gè)看起來(lái)有點(diǎn)難的函數(shù), 并不像我們想的直接判斷_disposeBlock后調(diào)用.

- (void)dispose {
    void (^disposeBlock)(void) = NULL;
    

    while (YES) {
        void *blockPtr = _disposeBlock;
        // 應(yīng)該又是多核作祟, 所以又要while YES, 又要?jiǎng)傎x值又compare
        // 這里blockPtr和_disposeBlock對(duì)比匹配后, 會(huì)把_disposeBlock賦值為NULL
        // 所以isDisposed只需要判斷_disposeBlock == NULL即可
        if (OSAtomicCompareAndSwapPtrBarrier(blockPtr, NULL, &_disposeBlock)) {
            // 如果不等于self, 說(shuō)明外面有block傳進(jìn)來(lái)
            if (blockPtr != (__bridge void *)self) {
                //CoreFoundation類型轉(zhuǎn)換為Objc類型
                disposeBlock = CFBridgingRelease(blockPtr);
            }

            break;
        }
    }
    // 執(zhí)行block(如果有)
    if (disposeBlock != nil) disposeBlock();
}

RACScopedDisposable

RACDisposable最后有個(gè)asScopedDisposable方法, 就是直接返回了一個(gè)RACScopedDisposable對(duì)象, 與父類的區(qū)別就是, 這個(gè)類的實(shí)例在dealloc時(shí)會(huì)調(diào)用dispose.

下面就是全部代碼, 還是比較明顯的

+ (instancetype)scopedDisposableWithDisposable:(RACDisposable *)disposable {
    // 自身沒(méi)有什么好釋放的, 直接調(diào)用參數(shù)的dispose
    return [self disposableWithBlock:^{
        [disposable dispose];
    }];
}

- (void)dealloc {
    [self dispose];
}

#pragma mark RACDisposable
// 重寫(xiě)父類的方法返回自己, 不然再次調(diào)用就又換一個(gè)實(shí)例
- (RACScopedDisposable *)asScopedDisposable {
    // totally already are
    return self;
}

RACKVOTrampoline

頭文件中介紹這類對(duì)象用于KVO觀察模式中. 所以最好還是和KVO相關(guān)的信號(hào)一起看, 但是代碼也并不復(fù)雜, 所以可以先行探討一下.

子類頭文件只有一個(gè)方法:

- (id)initWithTarget:(__weak NSObject *)target observer:(__weak NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block;

私有屬性:

@interface RACKVOTrampoline ()

// 正在觀察的keyPath
@property (nonatomic, readonly, copy) NSString *keyPath;

// 回調(diào)block
@property (nonatomic, readonly, copy) RACKVOBlock block;
// unsafe_unretained意味著這個(gè)屬性要純粹自己管理, 編譯器不會(huì)對(duì)這個(gè)屬性做任何ARC操作
@property (nonatomic, readonly, unsafe_unretained) NSObject *unsafeTarget;
@property (nonatomic, readonly, weak) NSObject *weakTarget;
@property (nonatomic, readonly, weak) NSObject *observer;

@end

方法實(shí)現(xiàn):

- (id)initWithTarget:(__weak NSObject *)target observer:(__weak NSObject *)observer keyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options block:(RACKVOBlock)block {
    NSCParameterAssert(keyPath != nil);
    NSCParameterAssert(block != nil);

    NSObject *strongTarget = target;
    if (strongTarget == nil) return nil;

    self = [super init];
    if (self == nil) return nil;

    _keyPath = [keyPath copy];

    _block = [block copy];
    _weakTarget = target;
    _unsafeTarget = strongTarget;
    _observer = observer;

    // 在RACKVOProxy.sharedProxy注冊(cè)觀察者 傳入context是過(guò)濾掉不屬于自己范圍的回調(diào), 里面涉及到RACKVOProxy.sharedProxy的實(shí)現(xiàn), 這個(gè)之后再看
    [RACKVOProxy.sharedProxy addObserver:self forContext:(__bridge void *)self];
    // 真正監(jiān)聽(tīng)的是RACKVOProxy.sharedProxy
    [strongTarget addObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath options:options context:(__bridge void *)self];

    // 在strongTarget和observer被釋放后, 要執(zhí)行自己的dispose
    [strongTarget.rac_deallocDisposable addDisposable:self];
    [self.observer.rac_deallocDisposable addDisposable:self];

    return self;
}

// 和scoped一樣, 在dealloc時(shí)會(huì)調(diào)用dispose
- (void)dealloc {
    [self dispose];
}

本身也在KVOProxy中注冊(cè)了觀察者, 所以肯定還有回調(diào)方法:


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if (context != (__bridge void *)self) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }

    RACKVOBlock block;
    id observer;
    id target;

    // 沒(méi)有什么特別的, 保證線程安全的情況下, 回調(diào)即可
    @synchronized (self) {
        block = self.block;
        observer = self.observer;
        target = self.weakTarget;
    }

    if (block == nil || target == nil) return;

    block(target, observer, change);
}

看看作為一個(gè)Disposable的關(guān)鍵方法:

- (void)dispose {
    NSObject *target;
    NSObject *observer;

    // 主要是清理持有的對(duì)象
    @synchronized (self) {
        _block = nil;
        // target雖然是unsafe的, 但是這個(gè)時(shí)候target應(yīng)該還未釋放, 因?yàn)樵趇nit方法中, 對(duì)target的rac_deallocDisposable進(jìn)行了add self的操作, 如果要釋放了, 則會(huì)自動(dòng)調(diào)用這方法
        target = self.unsafeTarget;
        observer = self.observer;

        _unsafeTarget = nil;
        _observer = nil;
    }

    // 移除添加的disposable
    [target.rac_deallocDisposable removeDisposable:self];
    [observer.rac_deallocDisposable removeDisposable:self];

    // 移除觀察者
    [target removeObserver:RACKVOProxy.sharedProxy forKeyPath:self.keyPath context:(__bridge void *)self];
    [RACKVOProxy.sharedProxy removeObserver:self forContext:(__bridge void *)self];
}

RACCompoundDisposable

本質(zhì)上可以看做一個(gè)Disposable數(shù)組, 在dispose的時(shí)候會(huì)把組合進(jìn)來(lái)的Disposable一個(gè)個(gè)調(diào)用dispose.

內(nèi)部的實(shí)現(xiàn)用了C數(shù)組和CoreFoundation的數(shù)組而不是一個(gè)Objc的Array, 原因應(yīng)該一是性能方面的考慮, 因?yàn)镃ompoundDisposable用的很頻繁, 二是會(huì)持有這個(gè)Disposable, 有一些類型的Disposable是在dealloc的時(shí)候要dispose的, 如果持有則會(huì)干擾這個(gè)行為.

同時(shí), 實(shí)現(xiàn)還用了一個(gè)魔數(shù)長(zhǎng)度的固定大小數(shù)組, 更多的disposable會(huì)額外存儲(chǔ)在一個(gè)CoreFoundation創(chuàng)建的數(shù)組中. 有2個(gè)問(wèn)題:

  1. 為什么要分為2個(gè)部分?
  2. 為什么這個(gè)數(shù)字是2?

第一個(gè)問(wèn)題的答案是性能, 用CoreFoundation是比較耗時(shí)的, 而直接用一個(gè)固定大小的C數(shù)組會(huì)更快.
第二個(gè)問(wèn)題的答案還是性能, 只不過(guò)這個(gè)是作者綜合測(cè)算出來(lái)的.

#define RACCompoundDisposableInlineCount 2

基于上面的緣故, 這里實(shí)現(xiàn)會(huì)看起來(lái)比較復(fù)雜一點(diǎn), 但是實(shí)際上還是比較好懂的, 看成對(duì)數(shù)組的操作即可.

- (id)initWithDisposables:(NSArray *)otherDisposables {
    self = [self init];
    if (self == nil) return nil;

    #if RACCompoundDisposableInlineCount
    [otherDisposables enumerateObjectsUsingBlock:^(RACDisposable *disposable, NSUInteger index, BOOL *stop) {
        // 前2個(gè)存在inline數(shù)組中, 也就是C數(shù)組
        _inlineDisposables[index] = disposable;
        // 超過(guò)2個(gè)的先不管
        if (index == RACCompoundDisposableInlineCount - 1) *stop = YES;
    }];
    #endif

    // 大于2個(gè)的創(chuàng)建CoreFoundation數(shù)組來(lái)管理
    if (otherDisposables.count > RACCompoundDisposableInlineCount) {
        _disposables = RACCreateDisposablesArray();
        // 寫(xiě)入數(shù)據(jù)
        CFRange range = CFRangeMake(RACCompoundDisposableInlineCount, (CFIndex)otherDisposables.count - RACCompoundDisposableInlineCount);
        CFArrayAppendArray(_disposables, (__bridge CFArrayRef)otherDisposables, range);
    }

    return self;
}

// dealloc不會(huì)自動(dòng)調(diào)用dispose
- (void)dealloc {
    #if RACCompoundDisposableInlineCount
    for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
        _inlineDisposables[i] = nil;
    }
    #endif

    if (_disposables != NULL) {
        CFRelease(_disposables);
        _disposables = NULL;
    }
}

添加/刪除Disposable的操作:

- (void)addDisposable:(RACDisposable *)disposable {
    NSCParameterAssert(disposable != self);
    if (disposable == nil || disposable.disposed) return;

    BOOL shouldDispose = NO;

    // 保證線程安全
    OSSpinLockLock(&_spinLock);
    {
        // 如果此時(shí)已經(jīng)dispose掉了, 那么也要對(duì)該disposable調(diào)用dispose
        if (_disposed) {
            shouldDispose = YES;
        } else {
            // 主體代碼:
            // 1. 先找找inline數(shù)組里面有沒(méi)有空位
            #if RACCompoundDisposableInlineCount
            for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
                if (_inlineDisposables[i] == nil) {
                    // 有就賦值, 然后跳到后面去執(zhí)行
                    _inlineDisposables[i] = disposable;
                    goto foundSlot;
                }
            }
            #endif
            // 2. 如果已經(jīng)滿額, 就加入到CoreFoundation中
            if (_disposables == NULL) _disposables = RACCreateDisposablesArray();
            CFArrayAppendValue(_disposables, (__bridge void *)disposable);

            // 下面是加入了DTrace探針的代碼? 
            if (RACCOMPOUNDDISPOSABLE_ADDED_ENABLED()) {
                RACCOMPOUNDDISPOSABLE_ADDED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount);
            }

        #if RACCompoundDisposableInlineCount
        foundSlot:;
        #endif
        }
    }
    OSSpinLockUnlock(&_spinLock);
    // 在鎖外面調(diào)用, 防止遞歸使用CompoundDisposable
    if (shouldDispose) [disposable dispose];
}

// remove的操作基本差不多, 主要是一些C函數(shù)的使用
- (void)removeDisposable:(RACDisposable *)disposable {
    if (disposable == nil) return;

    OSSpinLockLock(&_spinLock);
    {
        if (!_disposed) {
            #if RACCompoundDisposableInlineCount
            for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
                               // 這里既然已經(jīng)找到了, 為什么不跳到最后呢? 難道就是為了少寫(xiě)一對(duì)大括號(hào)么,[手動(dòng)摳鼻]
                if (_inlineDisposables[i] == disposable) _inlineDisposables[i] = nil;
            }
            #endif

            if (_disposables != NULL) {
                CFIndex count = CFArrayGetCount(_disposables);
                for (CFIndex i = count - 1; i >= 0; i--) {
                    const void *item = CFArrayGetValueAtIndex(_disposables, i);
                    if (item == (__bridge void *)disposable) {
                        CFArrayRemoveValueAtIndex(_disposables, i);
                    }
                }

                if (RACCOMPOUNDDISPOSABLE_REMOVED_ENABLED()) {
                    RACCOMPOUNDDISPOSABLE_REMOVED(self.description.UTF8String, disposable.description.UTF8String, CFArrayGetCount(_disposables) + RACCompoundDisposableInlineCount);
                }
            }
        }
    }
    OSSpinLockUnlock(&_spinLock);
}

至于核心的dispose, 顯然就會(huì)是2個(gè)數(shù)組的遍歷了:

- (void)dispose {
    // 1. 把2個(gè)數(shù)組copy出來(lái)到本地
    // 這么做一是可以保證線程安全(局部變量是線程安全的)
    // 而是可以盡早解開(kāi)鎖
    #if RACCompoundDisposableInlineCount
    RACDisposable *inlineCopy[RACCompoundDisposableInlineCount];
    #endif

    CFArrayRef remainingDisposables = NULL;

    OSSpinLockLock(&_spinLock);
    {
        _disposed = YES;

        #if RACCompoundDisposableInlineCount
        for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
            inlineCopy[i] = _inlineDisposables[i];
            _inlineDisposables[i] = nil;
        }
        #endif

        remainingDisposables = _disposables;
        _disposables = NULL;
    }
    OSSpinLockUnlock(&_spinLock);

    #if RACCompoundDisposableInlineCount
    // 遍歷inline數(shù)組 調(diào)用dispose
    for (unsigned i = 0; i < RACCompoundDisposableInlineCount; i++) {
        [inlineCopy[i] dispose];
    }
    #endif

    if (remainingDisposables == NULL) return;
    
    // 額外的Disposables要傳入函數(shù)指針來(lái)遍歷然后調(diào)用dispose
    CFIndex count = CFArrayGetCount(remainingDisposables);
    CFArrayApplyFunction(remainingDisposables, CFRangeMake(0, count), &disposeEach, NULL);
    CFRelease(remainingDisposables);
}

static void disposeEach(const void *value, void *context) {
    RACDisposable *disposable = (__bridge id)value;
    [disposable dispose];
}

整體來(lái)說(shuō)上面的代碼還是比較易懂的, 就是有很多C級(jí)別的代碼, 很多API都比較底層.

RACSerialDisposable

Signal中說(shuō)了對(duì)RACSerialDisposable調(diào)用setDisposable會(huì)有額外的一個(gè)效果, 舊的值如果已經(jīng)被dispose了, 那么新的值也會(huì)立即dispose掉.

這個(gè)類的頭文件也比較簡(jiǎn)單, 一個(gè)屬性2個(gè)方法, 私有屬性還有一些線程安全和狀態(tài)以及輔助操作的聲明(屬性聲明了disposable, 在拓展里面還要寫(xiě)RACDisposable * _disposable是因?yàn)閷?shí)現(xiàn)方法重寫(xiě)了其getter方法--也就是disposable方法, 而在里面又要訪問(wèn)這個(gè)值, 為了避免造成遞歸, 所以需要直接訪問(wèn), 而不能self.的形式訪問(wèn)).

直接看下這2個(gè)getter和setter

- (RACDisposable *)disposable {
    // 保證線程安全, 因?yàn)橛锌赡軙?huì)經(jīng)常變化
    RACDisposable *result;

    OSSpinLockLock(&_spinLock);
    result = _disposable;
    OSSpinLockUnlock(&_spinLock);

    return result;
}

- (void)setDisposable:(RACDisposable *)disposable {
    [self swapInDisposable:disposable];
}

// 核心方法
- (RACDisposable *)swapInDisposable:(RACDisposable *)newDisposable {
    RACDisposable *existingDisposable;
    BOOL alreadyDisposed;

    OSSpinLockLock(&_spinLock);
    // 判斷是否已經(jīng)dispose過(guò)
    alreadyDisposed = _disposed;
    if (!alreadyDisposed) {
        // 如果沒(méi)有, 則把新Disposable賦給屬性, 局部變量存儲(chǔ)老的Disposable
        existingDisposable = _disposable;
        _disposable = newDisposable;
    }
    OSSpinLockUnlock(&_spinLock);
    // 如果老的已經(jīng)dispose過(guò)了, 那么新的也直接dispose掉, 返回空
    if (alreadyDisposed) {
        [newDisposable dispose];
        return nil;
    }
    // 返回老的Disposable
    return existingDisposable;
}

dispose方法則沒(méi)什么特別:

- (void)dispose {
    RACDisposable *existingDisposable;

    OSSpinLockLock(&_spinLock);
    if (!_disposed) {
        existingDisposable = _disposable;
        _disposed = YES;
        _disposable = nil;
    }
    OSSpinLockUnlock(&_spinLock);
    
    [existingDisposable dispose];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末忠藤,一起剝皮案震驚了整個(gè)濱河市蔼两,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钠至,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件唱星,死亡現(xiàn)場(chǎng)離奇詭異峭沦,居然都是意外死亡治拿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén)捷绑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)韩脑,“玉大人,你說(shuō)我怎么就攤上這事粹污∪挪牛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵厕怜,是天一觀的道長(zhǎng)衩匣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)粥航,這世上最難降的妖魔是什么琅捏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮递雀,結(jié)果婚禮上柄延,老公的妹妹穿的比我還像新娘。我一直安慰自己缀程,他們只是感情好搜吧,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著杨凑,像睡著了一般滤奈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撩满,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天蜒程,我揣著相機(jī)與錄音,去河邊找鬼伺帘。 笑死昭躺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伪嫁。 我是一名探鬼主播领炫,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼张咳!你這毒婦竟也來(lái)了帝洪?” 一聲冷哼從身側(cè)響起似舵,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碟狞,沒(méi)想到半個(gè)月后啄枕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡族沃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年频祝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脆淹。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡常空,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盖溺,到底是詐尸還是另有隱情漓糙,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布烘嘱,位于F島的核電站昆禽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蝇庭。R本人自食惡果不足惜醉鳖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哮内。 院中可真熱鬧盗棵,春花似錦、人聲如沸北发。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)琳拨。三九已至瞭恰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間从绘,已是汗流浹背寄疏。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留僵井,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓驳棱,卻偏偏與公主長(zhǎng)得像批什,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子社搅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

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