OC底層原理17-KVO底層原理

iOS--OC底層原理文章匯總

KVO(Key-Value Observing)——鍵值觀察,它是一種機(jī)制湃鹊,它允許將其他對(duì)象的指定屬性的更改,通知給另一個(gè)對(duì)象镣奋。KVO蘋(píng)果文檔

關(guān)于KVO如何創(chuàng)建使用币呵,大致分為三個(gè)步驟:

使用步驟

注冊(cè)觀察者

// 定義兩個(gè)上下文
static void *PersonAccountBalanceContext = &PersonAccountBalanceContext;
static void *PersonAccountInterestRateContext = &PersonAccountInterestRateContext;

- (void)registerAsObserverForAccount:(Account*)account {
 
    [account addObserver:self
              forKeyPath:@"balance"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                 context:PersonAccountBalanceContext];
 
    [account addObserver:self
              forKeyPath:@"interestRate"
                 options:(NSKeyValueObservingOptionNew |
                          NSKeyValueObservingOptionOld)
                  context:PersonAccountInterestRateContext];
}

options 是一個(gè)枚舉侨颈,包含了以下四個(gè)值:
NSKeyValueObservingOptionNew:觀察更改后的值余赢;
NSKeyValueObservingOptionOld:觀察更改前的值;
NSKeyValueObservingOptionInitial:觀察最初的值(在注冊(cè)觀察服務(wù)時(shí)會(huì)調(diào)用一次觸發(fā)方法)哈垢;
NSKeyValueObservingOptionPrior:分別在值修改前后觸發(fā)方法(即一次修改有兩次觸發(fā))

context:上下文妻柒,包含任意數(shù)據(jù),這些數(shù)據(jù)將在相應(yīng)的更改通知中傳遞回觀察者耘分【偎可以指定NULL并完全依賴(lài)KeyPath字符串來(lái)確定更改通知的來(lái)源绑警,但是這種方法可能會(huì)導(dǎo)致對(duì)象的父類(lèi)由于不同的原因而觀察到相同的鍵路徑,從而導(dǎo)致問(wèn)題央渣。

一種更安全待秃,更可擴(kuò)展的方法是使用上下文確保您收到的通知是發(fā)給觀察者的,而不是超類(lèi)的痹屹。在類(lèi)中定義唯一命名的靜態(tài)變量的地址章郁,就滿足了良好的上下文條件。在父類(lèi)或子類(lèi)中以類(lèi)似方式選擇的上下文不太可能重疊志衍∨可以為整個(gè)類(lèi)選擇一個(gè)上下文,然后依靠通知消息中的KeyPath字符串來(lái)確定更改的內(nèi)容楼肪。另外培廓,可以為每個(gè)觀察到的鍵路徑創(chuàng)建一個(gè)不同的上下文,從而完全不需要進(jìn)行字符串比較春叫,從而可以更有效地進(jìn)行通知解析肩钠。上面示例中顯示了以這種方式選擇的balanceinterestRate屬性的示例上下文。

接受變更通知

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
 
    if (context == PersonAccountBalanceContext) {
        // Do something with the balance…
 
    } else if (context == PersonAccountInterestRateContext) {
        // Do something with the interest rate…
 
    } else {
        // Any unrecognized context must belong to super
        [super observeValueForKeyPath:keyPath
                             ofObject:object
                               change:change
                               context:context];
    }
}

當(dāng)對(duì)象的觀察屬性的值更改時(shí)价匠,觀察者會(huì)收到一條observeValueForKeyPath:ofObject:change:context: 消息。所有觀察者都必須實(shí)現(xiàn)此方法呛每。

移除觀察者

  • 當(dāng)觀察者不再應(yīng)接收消息時(shí)踩窖,使用該方法removeObserver:forKeyPath:注銷(xiāo)觀察者。至少在觀察者從內(nèi)存釋放之前調(diào)用注銷(xiāo)方法晨横,否則會(huì)導(dǎo)致奔潰洋腮。
- (void)unregisterAsObserverForAccount:(Account*)account {
    [account removeObserver:self
                 forKeyPath:@"balance"
                    context:PersonAccountBalanceContext];
 
    [account removeObserver:self
                 forKeyPath:@"interestRate"
                    context:PersonAccountInterestRateContext];
}
注銷(xiāo)觀察者

典型的使用場(chǎng)景是在觀察者初始化期間(例如,在initviewDidLoad)注冊(cè)為觀察者手形,在釋放過(guò)程中(通常在中dealloc)解除注冊(cè)啥供,以確保成對(duì)和有序地添加和刪除消息,并確保觀察者在從內(nèi)存中釋放之前被取消注冊(cè)库糠。
如果注冊(cè)了觀察者未注銷(xiāo)伙狐,當(dāng)再次進(jìn)入觀察者界面時(shí),會(huì)再次注冊(cè)KVO觀察者曼玩,導(dǎo)致KVO觀察的重復(fù)注冊(cè)鳞骤,而第一次的通知對(duì)象還在內(nèi)存中,沒(méi)有進(jìn)行釋放黍判。如果此時(shí)接收到了屬性值變化的通知豫尽,會(huì)出現(xiàn)找不到原有的通知對(duì)象,只能找到現(xiàn)有的通知對(duì)象顷帖,即第二次KVO注冊(cè)的觀察者美旧,將會(huì)導(dǎo)致類(lèi)似野指針的崩潰渤滞,可理解為一直保持著一個(gè)野通知,且一直在監(jiān)聽(tīng)榴嗅。

問(wèn):多次添加注冊(cè)未注銷(xiāo)會(huì)不會(huì)造成循環(huán)引用妄呕?不會(huì),因?yàn)?code>observer在底層的字符串是weak修飾,所以不會(huì)導(dǎo)致循環(huán)引用嗽测。

自動(dòng) & 手動(dòng)變更通知

自動(dòng)變更通知

NSObject提供自動(dòng)的鍵值更改通知的基本實(shí)現(xiàn)绪励。自動(dòng)鍵值更改通知將使用鍵值兼容訪問(wèn)器(setName)以及鍵值編碼方法(setValue:forKey:)進(jìn)行的更改通知給觀察者。由mutableArrayValueForKey:返回的收集代理對(duì)象也支持自動(dòng)通知唠粥。
以下顯示的示例使該屬性的所有觀察者都name收到有關(guān)更改的通知疏魏。

// 使用setter方法直接設(shè)置
[account setName:@"Savings"];
// 使用kvc設(shè)置name
[account setValue:@"Savings" forKey:@"name"];
 
// 使用keypath 設(shè)置document的name
[document setValue:@"Savings" forKeyPath:@"account.name"];
 
// 使用 mutableArrayValueForKey: to retrieve a relationship proxy object.
NSArray * arrayTrans = @{@"1001",@"1002"};
NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];
[transactions addObject: arrayTrans];

手動(dòng)變更通知

這是切換手動(dòng)or自動(dòng)的方法,默認(rèn)YES即為自動(dòng)變更通知晤愧,這里可以判斷theKey來(lái)控制是否手動(dòng)變更通知大莫。

+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
    
    BOOL automatic = NO;
    if ([theKey isEqualToString:@"balance"]) {
        automatic = NO;
    }
    else {
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}

要實(shí)現(xiàn)手動(dòng)觀察者通知,在willChangeValueForKey:更改值之前和didChangeValueForKey:更改值之后調(diào)用官份。如下實(shí)現(xiàn)了該balance屬性的手動(dòng)通知,首先檢查值是否已更改來(lái)最大程度地減少發(fā)送不必要的通知只厘。如下balance則可以實(shí)現(xiàn)僅在通知已更改時(shí)才提供通知。

- (void)setBalance:(double)theBalance {
    if (theBalance != _balance) {
        [self willChangeValueForKey:@"balance"];
        _balance = theBalance;
        [self didChangeValueForKey:@"balance"];
    }
}

如果一次操作多個(gè)更改時(shí)舅巷,就需要嵌套了多個(gè)鍵的更改通知羔味,如下

- (void)setBalance:(double)theBalance {
    [self willChangeValueForKey:@"balance"];
    [self willChangeValueForKey:@"itemChanged"];
    _balance = theBalance;
    _itemChanged = _itemChanged+1;
    [self didChangeValueForKey:@"itemChanged"];
    [self didChangeValueForKey:@"balance"];
}

如果是有序的一對(duì)多關(guān)系,不僅必須指定已更改的鍵悄谐,還必須指定更改的類(lèi)型和所涉及對(duì)象的索引介评。NSKeyValueChange類(lèi)型變化的有:NSKeyValueChangeInsertionNSKeyValueChangeRemovalNSKeyValueChangeReplacement爬舰。受影響對(duì)象的索引作為NSIndexSet對(duì)象傳遞。

- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"transactions"];
 
    // 刪除指定索引的事務(wù)對(duì)象寒瓦。
 
    [self didChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"transactions"];
}

觀察多屬性變化

注冊(cè)一個(gè)觀察者情屹,觀察多個(gè)屬性的變化。舉例:有一個(gè)我下載進(jìn)度杂腰,每次點(diǎn)擊屏幕觸發(fā)屬性值增加垃你,觀察currentData,totalData的變化喂很,利用keyPathsForValuesAffectingValueForKey通過(guò)keyPath拼接的方式觀察兩個(gè)屬性值的變化惜颇,當(dāng)觀察到變化的值后,打印出變化后的值少辣。

//1凌摄、觀察一個(gè)數(shù)組 ,數(shù)組包含兩個(gè)屬性:currentData totalData
// ---Person.m---
+ (NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
    
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
    if ([key isEqualToString:@"downloadProgress"]) {
        NSArray *affectingKeys = @[@"totalData", @"currentData"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}
// 更改下載進(jìn)度數(shù)值
- (NSString *)downloadProgress{
    if (self.writtenData == 0) {
        self.writtenData = 10;
    }
    if (self.totalData == 0) {
        self.totalData = 100;
    }
    return [[NSString alloc] initWithFormat:@"%f",1.0f*self.writtenData/self.totalData];
}


// ----ViewController.m----

- (void)viewDidLoad {
    [super viewDidLoad];
  //2、注冊(cè)KVO觀察
  [self.person addObserver:self forKeyPath:@"downloadProgress" options:(NSKeyValueObservingOptionNew) context:NULL];
}
//3漓帅、觸發(fā)屬性值增加
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    self.person.currentData += 10;
    self.person.totalData  += 1;
}
//4锨亏、收到變更通知
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"%@",change);
}
//4痴怨、移除觀察者
- (void)dealloc{
    [self.person removeObserver:self forKeyPath:@"downloadProgress"];
}

觀察可變數(shù)組

觀察可變數(shù)組類(lèi)型,用到的是mutableArrayValueForKey ormutableArrayValueForKeyPath.

// 1器予、注冊(cè)可變數(shù)組KVO觀察者
- (void)viewDidLoad {
    [super viewDidLoad];
    self.account.dateArray = [NSMutableArray arrayWithCapacity:10];
    [self.account addObserver:self forKeyPath:@"dateArray" options:(NSKeyValueObservingOptionNew) context:NULL];
}
// 2浪藻、接收變更通知
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSLog(@"%@",change);
}

// 3、移除觀察者
- (void)dealloc{
  [self.account removeObserver:self forKeyPath:@"dateArray"];
}

// 4乾翔、給數(shù)組添加數(shù)據(jù)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    // 
    [[self.person mutableArrayValueForKey:@"dateArray"] addObject:@"1"];
}

KVO觀察屬性爱葵,不觀察成員變量

定義一個(gè)類(lèi),分別觀察其屬性name和成員變量nickeName的變化

    self.account = [[Account alloc] init];
    [self.account addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
    [self.account addObserver:self forKeyPath:@"nickName" options:NSKeyValueObservingOptionNew context:NULL];

觸發(fā)對(duì)屬性和成員變量的賦值反浓,執(zhí)行結(jié)果如下:

觀察屬性vs成員

KVO在對(duì)屬性萌丈、成員變量觀察時(shí),只觀察到了屬性的變化勾习。原因是屬性比成員變量多setter方法浓瞪,而KVO觀察的就是setter方法。

中間類(lèi)

蘋(píng)果關(guān)于KVO實(shí)現(xiàn)的的解釋
KVO實(shí)現(xiàn)解釋
  1. 自動(dòng)鍵值觀察是使用是基于isa-swizzling(指針裝換)的技術(shù)實(shí)現(xiàn)的巧婶。
  2. isa指針乾颁,顧名思義,指向?qū)ο蟮念?lèi)艺栈,它保持一個(gè)調(diào)度表英岭。該調(diào)度表實(shí)質(zhì)上包含指向該類(lèi)實(shí)現(xiàn)的方法的指針以及其他數(shù)據(jù)。
  3. 在為對(duì)象的屬性注冊(cè)觀察者時(shí)湿右,將修改觀察對(duì)象的isa指針诅妹,它指向了中間類(lèi)而不是真實(shí)類(lèi)。因此毅人,isa指針的值不一定反映實(shí)例的實(shí)際類(lèi)吭狡。
  4. 不要依靠isa指針來(lái)確定類(lèi)的成員變量。相反丈莺,應(yīng)該使用該類(lèi)方法確定對(duì)象實(shí)例的類(lèi)划煮。

中間類(lèi)的產(chǎn)生

根據(jù)這段文字描述,isa指針在為對(duì)象的屬性注冊(cè)觀察者時(shí)缔俄,觀察對(duì)象的isa指針會(huì)發(fā)生改邊弛秋,指向了一個(gè)中間類(lèi)±兀可以做一個(gè)簡(jiǎn)單的探究蟹略。

以觀察剛才的account對(duì)象屬性name為例,探究在添加觀察者之后類(lèi)是否發(fā)生了變化

產(chǎn)生了一個(gè)中間類(lèi)

由上面的結(jié)果知道遏佣,在注冊(cè)觀察者之前挖炬,對(duì)象的類(lèi)是Account,注冊(cè)之后贼急,實(shí)例對(duì)象的指針地址發(fā)生了變化茅茂,指向了一個(gè)中間類(lèi)NSKVONotifying_Account捏萍。

中間類(lèi)是否為子類(lèi)?

不禁好奇這個(gè)NSKVONotifying_Account中間類(lèi)是怎樣一個(gè)存在空闲,是否為Account的子類(lèi)呢令杈?可以試著打印account對(duì)象的類(lèi)探究下

// ---------------
    self.account = [[Account alloc] init];
    [self printClasses:self.account.class];
    [self.account addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NULL];
    [self printClasses:self.account.class];
//  ---------------

#pragma mark - 遍歷類(lèi)以及子類(lèi)
- (void)printClasses:(Class)cls{
    // 注冊(cè)類(lèi)的總數(shù)
    int count = objc_getClassList(NULL, 0);
    // 創(chuàng)建一個(gè)數(shù)組, 其中包含給定對(duì)象
    NSMutableArray *mArray = [NSMutableArray arrayWithObject:cls];
    // 獲取所有已注冊(cè)的類(lèi)
    Class* classes = (Class*)malloc(sizeof(Class)*count);
    objc_getClassList(classes, count);
    for (int i = 0; i<count; i++) {
        if (cls == class_getSuperclass(classes[i])) {
            [mArray addObject:classes[I]];
        }
    }
    free(classes);
    NSLog(@"classes = %@", mArray);
}

打印之

中間類(lèi)是Account的子類(lèi)

根據(jù)結(jié)果可以知道:NSKVONotifying_Account中間類(lèi)是Account的子類(lèi)碴倾。

中間類(lèi)的方法

接下來(lái)逗噩,順藤摸瓜,可以探究下中間類(lèi)中有什么方法跌榔?
定義一個(gè)打印方法列表的方法

#pragma mark - 遍歷方法-ivar-property
- (void)printClassAllMethod:(Class)cls{
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(cls, &count);
    for (int i = 0; i<count; i++) {
        Method method = methodList[I];
        SEL sel = method_getName(method);
        IMP imp = class_getMethodImplementation(cls, sel);
        NSLog(@"%@-%p",NSStringFromSelector(sel),imp);
    }
    free(methodList);
}

同理再次執(zhí)行异雁,分析一下打印結(jié)果

中間類(lèi)的四個(gè)方法

中間類(lèi)打印出四個(gè)方法:setName、class僧须、dealloc纲刀、_isKVOA,為了方便研究是什么担平,
在Acount類(lèi)中添加兩個(gè)實(shí)例方法:

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Account : NSObject
{
    NSString * nickName;
}
@property (nonatomic,copy) NSString * name;

-(void)method1;
-(void)method2;

@end

NS_ASSUME_NONNULL_END

account添加一個(gè)子類(lèi)SubAccount,只重寫(xiě)父類(lèi)namesetter方法示绊,其他只集成;

#import "SubAccount.h"

@implementation SubAccount
// 重寫(xiě)父類(lèi)是setName方法
- (void)setName:(NSString *)name
{
    
}
@end

再執(zhí)行分析結(jié)果

子類(lèi) vs 中間類(lèi) 方法列表

根據(jù)以上可以知道,SubAccount集成了Account類(lèi),并且重寫(xiě)了setName方法暂论,在遍歷子類(lèi)方法列表時(shí)面褐,只打印出了setName方法。說(shuō)明NSKVONotifying_Account是重寫(xiě)了父類(lèi)Account的四個(gè)方法取胎,這四個(gè)方法是否真的是Account的呢展哭?
為了研究這個(gè),我們可以打印出Account目前的方法列表

由上面的結(jié)果可以知道闻蛀,NSKVONotifying_Account只重寫(xiě)了setName的方法匪傍,剩下的class、dealloc觉痛、_isKVOA則是重寫(xiě)了Account父類(lèi)NSObject的方法析恢。

isa指針重指向觀察者類(lèi)

經(jīng)過(guò)探究,在移除觀察者時(shí)秧饮,isa指針重指向了Account類(lèi)

注銷(xiāo)前后

在調(diào)用注銷(xiāo)方法之后,觀察者對(duì)象的中間類(lèi)消失泽篮,實(shí)例對(duì)象的類(lèi)還原成Account盗尸,那這個(gè)中間類(lèi)是否是真的消失了?

ViewController頁(yè)面我們介入一個(gè)打印Account類(lèi)及子類(lèi)的方法帽撑,第一次進(jìn)入程序時(shí)泼各,打印它,之后進(jìn)入測(cè)試KVO界面亏拉,打印出注冊(cè)觀察者前后Account的類(lèi)變化,最后注銷(xiāo)觀察者前后的Account的類(lèi)變化扣蜻,再次回到ViewController頁(yè)面逆巍,再次打印之。

image.png

可以知道莽使,中間類(lèi)NSKVONotifying_Account并未隨著KVO界面消息锐极、注銷(xiāo)觀察者之后就消失,它依然還是作為Account的子類(lèi)芳肌,在其開(kāi)辟的內(nèi)存空間里面灵再,這樣的目的就是為了復(fù)用

總結(jié)

  1. 給實(shí)例對(duì)象注冊(cè)KVO觀察者后亿笤,實(shí)例對(duì)象的指針地址會(huì)發(fā)生變化,會(huì)生成一個(gè)實(shí)例對(duì)象父類(lèi)的一個(gè)子類(lèi)NSKVONotifying_xxxx;
  2. 中間類(lèi)方法會(huì)重寫(xiě)父類(lèi)的方法翎迁;其中class、dealloc净薛、_isKVOA則是重寫(xiě)了基類(lèi)NSObject的方法汪榔。(dealloc:重寫(xiě)了釋放方法、_isKVOA:判斷是否為KVO類(lèi))
  3. 移除觀察者后肃拜,觀察者類(lèi)會(huì)還原為初始化類(lèi)(isa指針重指向初始化類(lèi))痴腌;但中間類(lèi)并未從內(nèi)存中移除,目的是為了方便復(fù)用爆班。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末衷掷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子柿菩,更是在濱河造成了極大的恐慌戚嗅,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枢舶,死亡現(xiàn)場(chǎng)離奇詭異懦胞,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)凉泄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)躏尉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人后众,你說(shuō)我怎么就攤上這事胀糜。” “怎么了蒂誉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵教藻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我右锨,道長(zhǎng)括堤,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮悄窃,結(jié)果婚禮上讥电,老公的妹妹穿的比我還像新娘。我一直安慰自己轧抗,他們只是感情好恩敌,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著鸦致,像睡著了一般潮剪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上分唾,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天抗碰,我揣著相機(jī)與錄音,去河邊找鬼绽乔。 笑死弧蝇,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的折砸。 我是一名探鬼主播看疗,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼睦授!你這毒婦竟也來(lái)了两芳?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤去枷,失蹤者是張志新(化名)和其女友劉穎怖辆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體删顶,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竖螃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逗余。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片特咆。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖录粱,靈堂內(nèi)的尸體忽然破棺而出腻格,到底是詐尸還是另有隱情,我是刑警寧澤啥繁,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布荒叶,位于F島的核電站,受9級(jí)特大地震影響输虱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脂凶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一宪睹、第九天 我趴在偏房一處隱蔽的房頂上張望愁茁。 院中可真熱鬧,春花似錦亭病、人聲如沸鹅很。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)促煮。三九已至,卻和暖如春整袁,著一層夾襖步出監(jiān)牢的瞬間菠齿,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工坐昙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绳匀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓炸客,卻偏偏與公主長(zhǎng)得像疾棵,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痹仙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345