Objective-C Associated Objects 的實(shí)現(xiàn)原理

我們知道躺彬,在 Objective-C 中可以通過(guò) Category 給一個(gè)現(xiàn)有的類添加屬性,但是卻不能添加實(shí)例變量倍权,這似乎成為了 Objective-C 的一個(gè)明顯短板悯仙。然而值得慶幸的是,我們可以通過(guò) Associated Objects 來(lái)彌補(bǔ)這一不足想诅。本文將結(jié)合runtime源碼深入探究 Objective-C 中 Associated Objects 的實(shí)現(xiàn)原理召庞。

在閱讀本文的過(guò)程中,讀者需要著重關(guān)注以下三個(gè)問(wèn)題:

關(guān)聯(lián)對(duì)象被存儲(chǔ)在什么地方来破,是不是存放在被關(guān)聯(lián)對(duì)象本身的內(nèi)存中篮灼?

關(guān)聯(lián)對(duì)象的五種關(guān)聯(lián)策略有什么區(qū)別,有什么坑徘禁?

關(guān)聯(lián)對(duì)象的生命周期是怎樣的诅诱,什么時(shí)候被釋放,什么時(shí)候被移除送朱?

這是我寫(xiě)這篇文章的初衷娘荡,也是本文的價(jià)值所在。

使用場(chǎng)景

按照 Mattt Thompson 大神的文章Associated Objects中的說(shuō)法驶沼,Associated Objects 主要有以下三個(gè)使用場(chǎng)景:

為現(xiàn)有的類添加私有變量以幫助實(shí)現(xiàn)細(xì)節(jié)炮沐;

為現(xiàn)有的類添加公有屬性;

為 KVO 創(chuàng)建一個(gè)關(guān)聯(lián)的觀察者商乎。

從本質(zhì)上看央拖,第 1 、2 個(gè)場(chǎng)景其實(shí)是一個(gè)意思鹉戚,唯一的區(qū)別就在于新添加的這個(gè)屬性是公有的還是私有的而已鲜戒。就目前來(lái)說(shuō),我在實(shí)際工作中使用得最多的是第 2 個(gè)場(chǎng)景抹凳,而第 3 個(gè)場(chǎng)景我還沒(méi)有使用過(guò)遏餐。

相關(guān)函數(shù)

與 Associated Objects 相關(guān)的函數(shù)主要有三個(gè),我們可以在 runtime 源碼的 runtime.h 文件中找到它們的聲明:

1

2

3void?objc_setAssociatedObject(id?object,?const?void?*key,?id?value,?objc_AssociationPolicy?policy);

id?objc_getAssociatedObject(id?object,?const?void?*key);

void?objc_removeAssociatedObjects(id?object);

這三個(gè)函數(shù)的命名對(duì)程序員非常友好赢底,可以讓我們一眼就看出函數(shù)的作用:

objc_setAssociatedObject 用于給對(duì)象添加關(guān)聯(lián)對(duì)象失都,傳入 nil 則可以移除已有的關(guān)聯(lián)對(duì)象;

objc_getAssociatedObject 用于獲取關(guān)聯(lián)對(duì)象幸冻;

objc_removeAssociatedObjects 用于移除一個(gè)對(duì)象的所有關(guān)聯(lián)對(duì)象屯烦。

注:objc_removeAssociatedObjects 函數(shù)我們一般是用不上的贩耐,因?yàn)檫@個(gè)函數(shù)會(huì)移除一個(gè)對(duì)象的所有關(guān)聯(lián)對(duì)象津函,將該對(duì)象恢復(fù)成“原始”狀態(tài)张抄。這樣做就很有可能把別人添加的關(guān)聯(lián)對(duì)象也一并移除,這并不是我們所希望的碑定。所以一般的做法是通過(guò)給 objc_setAssociatedObject 函數(shù)傳入 nil 來(lái)移除某個(gè)已有的關(guān)聯(lián)對(duì)象流码。

key 值

關(guān)于前兩個(gè)函數(shù)中的 key 值是我們需要重點(diǎn)關(guān)注的一個(gè)點(diǎn)又官,這個(gè) key 值必須保證是一個(gè)對(duì)象級(jí)別(為什么是對(duì)象級(jí)別?看完下面的章節(jié)你就會(huì)明白了)的唯一常量漫试。一般來(lái)說(shuō)六敬,有以下三種推薦的 key 值:

聲明 static char kAssociatedObjectKey; ,使用 &kAssociatedObjectKey 作為 key 值;

聲明 static void *kAssociatedObjectKey = &kAssociatedObjectKey; 驾荣,使用 kAssociatedObjectKey 作為 key 值外构;

用 selector ,使用 getter 方法的名稱作為 key 值秘车。

我個(gè)人最喜歡的(沒(méi)有之一)是第 3 種方式典勇,因?yàn)樗〉袅艘粋€(gè)變量名,非常優(yōu)雅地解決了計(jì)算科學(xué)中的兩大世界難題之一(命名)叮趴。

關(guān)聯(lián)策略

在給一個(gè)對(duì)象添加關(guān)聯(lián)對(duì)象時(shí)有五種關(guān)聯(lián)策略可供選擇:

其中,第 2 種與第 4 種权烧、第 3 種與第 5 種關(guān)聯(lián)策略的唯一差別就在于操作是否具有原子性眯亦。由于操作的原子性不在本文的討論范圍內(nèi),所以下面的實(shí)驗(yàn)和討論就以前三種以例進(jìn)行展開(kāi)般码。

實(shí)現(xiàn)原理

在探究 Associated Objects 的實(shí)現(xiàn)原理前妻率,我們還是先來(lái)動(dòng)手做一個(gè)小實(shí)驗(yàn),研究一下關(guān)聯(lián)對(duì)象什么時(shí)候會(huì)被釋放板祝。本實(shí)驗(yàn)主要涉及 ViewController 類和它的分類 ViewController+AssociatedObjects 宫静。注:本實(shí)驗(yàn)的完整代碼可以在這里AssociatedObjects找到,其中關(guān)鍵代碼如下:

@interface?ViewController?(AssociatedObjects)

@property?(assign,?nonatomic)?NSString?*associatedObject_assign;

@property?(strong,?nonatomic)?NSString?*associatedObject_retain;

@property?(copy,???nonatomic)?NSString?*associatedObject_copy;

@end

@implementation?ViewController?(AssociatedObjects)

-?(NSString?*)associatedObject_assign?{

returnobjc_getAssociatedObject(self,?_cmd);

}

-?(void)setAssociatedObject_assign:(NSString?*)associatedObject_assign?{

objc_setAssociatedObject(self,?@selector(associatedObject_assign),?associatedObject_assign,?OBJC_ASSOCIATION_ASSIGN);

}

-?(NSString?*)associatedObject_retain?{

returnobjc_getAssociatedObject(self,?_cmd);

}

-?(void)setAssociatedObject_retain:(NSString?*)associatedObject_retain?{

objc_setAssociatedObject(self,?@selector(associatedObject_retain),?associatedObject_retain,?OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

-?(NSString?*)associatedObject_copy?{

returnobjc_getAssociatedObject(self,?_cmd);

}

-?(void)setAssociatedObject_copy:(NSString?*)associatedObject_copy?{

objc_setAssociatedObject(self,?@selector(associatedObject_copy),?associatedObject_copy,?OBJC_ASSOCIATION_COPY_NONATOMIC);

}

@end

在 ViewController+AssociatedObjects.h 中聲明了三個(gè)屬性券时,限定符分別為 assign, nonatomic 孤里、strong, nonatomic 和 copy, nonatomic ,而在 ViewController+AssociatedObjects.m 中相應(yīng)的分別用 OBJC_ASSOCIATION_ASSIGN 橘洞、OBJC_ASSOCIATION_RETAIN_NONATOMIC 捌袜、OBJC_ASSOCIATION_COPY_NONATOMIC 三種關(guān)聯(lián)策略為這三個(gè)屬性添加“實(shí)例變量”。

__weak?NSString?*string_weak_assign?=?nil;

__weak?NSString?*string_weak_retain?=?nil;

__weak?NSString?*string_weak_copy???=?nil;

@implementation?ViewController

-?(void)viewDidLoad?{

[superviewDidLoad];

self.associatedObject_assign?=?[NSString?stringWithFormat:@"leichunfeng1"];

self.associatedObject_retain?=?[NSString?stringWithFormat:@"leichunfeng2"];

self.associatedObject_copy???=?[NSString?stringWithFormat:@"leichunfeng3"];

string_weak_assign?=?self.associatedObject_assign;

string_weak_retain?=?self.associatedObject_retain;

string_weak_copy???=?self.associatedObject_copy;

}

-?(void)touchesBegan:(NSSet?*)touches?withEvent:(UIEvent?*)event?{

//????NSLog(@"self.associatedObject_assign:?%@",?self.associatedObject_assign);?//?Will?Crash

NSLog(@"self.associatedObject_retain:?%@",?self.associatedObject_retain);

NSLog(@"self.associatedObject_copy:???%@",?self.associatedObject_copy);

}

@end

在 ViewController 的 viewDidLoad 方法中炸枣,我們對(duì)三個(gè)屬性進(jìn)行了賦值虏等,并聲明了三個(gè)全局的 __weak 變量來(lái)觀察相應(yīng)對(duì)象的釋放時(shí)機(jī)。此外适肠,我們重寫(xiě)了 touchesBegan:withEvent: 方法霍衫,在方法中分別打印了這三個(gè)屬性的當(dāng)前值。

在繼續(xù)閱讀下面章節(jié)前侯养,建議讀者先自行思考一下 self.associatedObject_assign 敦跌、self.associatedObject_retain 和 self.associatedObject_copy 指向的對(duì)象分別會(huì)在什么時(shí)候被釋放,以加深理解沸毁。

實(shí)驗(yàn)

我們先在 viewDidLoad 方法的第 28 行打上斷點(diǎn)峰髓,然后運(yùn)行程序傻寂,點(diǎn)擊導(dǎo)航欄右上角的按鈕 Push 到 ViewController 界面,程序?qū)⑼T跀帱c(diǎn)處携兵。接著疾掰,我們使用 lldb 的 watchpoint 命令來(lái)設(shè)置觀察點(diǎn),觀察全局變量 string_weak_assign 徐紧、string_weak_retain 和 string_weak_copy 的值的變化静檬。正確設(shè)置好觀察點(diǎn)后,將會(huì)在 console 中看到如下的類似輸出:

點(diǎn)擊繼續(xù)運(yùn)行按鈕并级,有一個(gè)觀察點(diǎn)將被命中拂檩。我們先查看 console 中的輸出,通過(guò)將這一步打印的 old value 和上一步的 new value 進(jìn)行對(duì)比嘲碧,我們可以知道本次命中的觀察點(diǎn)是 string_weak_assign 稻励,string_weak_assign 的值變成了 0x0000000000000000 ,也就是 nil 愈涩。換句話說(shuō) self.associatedObject_assign 指向的對(duì)象已經(jīng)被釋放了望抽,而通過(guò)查看左側(cè)調(diào)用棧我們可以知道,這個(gè)對(duì)象是由于其所在的 autoreleasepool 被 drain 而被釋放的履婉,這與我前面的文章《Objective-C Autorelease Pool 的實(shí)現(xiàn)原理》中的表述是一致的煤篙。提示,待會(huì)你也可以放開(kāi) touchesBegan:withEvent: 中第 31 行的注釋毁腿,在 ViewController 出現(xiàn)后辑奈,點(diǎn)擊一下它的 view ,進(jìn)一步驗(yàn)證一下這個(gè)結(jié)論已烤。

接下來(lái)鸠窗,我們點(diǎn)擊 ViewController 導(dǎo)航欄左上角的按鈕,返回前一個(gè)界面草戈,此時(shí)塌鸯,又將有一個(gè)觀察點(diǎn)被命中。同理唐片,我們可以知道這個(gè)觀察點(diǎn)是 string_weak_retain 丙猬。我們查看左側(cè)的調(diào)用棧,將會(huì)發(fā)現(xiàn)一個(gè)非常敏感的函數(shù)調(diào)用 _object_remove_assocations 费韭,調(diào)用這個(gè)函數(shù)后 ViewController 的所有關(guān)聯(lián)對(duì)象被全部移除茧球。最終,self.associatedObject_retain 指向的對(duì)象被釋放星持。

點(diǎn)擊繼續(xù)運(yùn)行按鈕抢埋,最后一個(gè)觀察點(diǎn) string_weak_copy 被命中。同理,self.associatedObject_copy 指向的對(duì)象也由于關(guān)聯(lián)對(duì)象的移除被最終釋放揪垄。

結(jié)論

由這個(gè)實(shí)驗(yàn)穷吮,我們可以得出以下結(jié)論:

關(guān)聯(lián)對(duì)象的釋放時(shí)機(jī)與被移除的時(shí)機(jī)并不總是一致的,比如上面的 self.associatedObject_assign 所指向的對(duì)象在 ViewController 出現(xiàn)后就被釋放了饥努,但是 self.associatedObject_assign 仍然有值捡鱼,還是保存的原對(duì)象的地址。如果之后再使用 self.associatedObject_assign 就會(huì)造成 Crash 酷愧,所以我們?cè)谑褂萌跻玫年P(guān)聯(lián)對(duì)象時(shí)要非常小心驾诈;

一個(gè)對(duì)象的所有關(guān)聯(lián)對(duì)象是在這個(gè)對(duì)象被釋放時(shí)調(diào)用的 _object_remove_assocations 函數(shù)中被移除的。

接下來(lái)溶浴,我們就一起看看 runtime 中的源碼乍迄,來(lái)驗(yàn)證下我們的實(shí)驗(yàn)結(jié)論。

objc_setAssociatedObject

我們可以在 objc-references.mm 文件中找到 objc_setAssociatedObject 函數(shù)最終調(diào)用的函數(shù):

void?_object_set_associative_reference(id?object,?void?*key,?id?value,?uintptr_t?policy)?{

//?retain?the?new?value?(if?any)?outside?the?lock.

ObjcAssociation?old_association(0,?nil);

id?new_value?=?value???acquireValue(value,?policy)?:?nil;

{

AssociationsManager?manager;

AssociationsHashMap?&associations(manager.associations());

disguised_ptr_t?disguised_object?=?DISGUISE(object);

if(new_value)?{

//?break?any?existing?association.

AssociationsHashMap::iterator?i?=?associations.find(disguised_object);

if(i?!=?associations.end())?{

//?secondary?table?exists

ObjectAssociationMap?*refs?=?i->second;

ObjectAssociationMap::iterator?j?=?refs->find(key);

if(j?!=?refs->end())?{

old_association?=?j->second;

j->second?=?ObjcAssociation(policy,?new_value);

}else{

(*refs)[key]?=?ObjcAssociation(policy,?new_value);

}

}else{

//?create?the?new?association?(first?time).

ObjectAssociationMap?*refs?=newObjectAssociationMap;

associations[disguised_object]?=?refs;

(*refs)[key]?=?ObjcAssociation(policy,?new_value);

object->setHasAssociatedObjects();

}

}else{

//?setting?the?association?to?nil?breaks?the?association.

AssociationsHashMap::iterator?i?=?associations.find(disguised_object);

if(i?!=??associations.end())?{

ObjectAssociationMap?*refs?=?i->second;

ObjectAssociationMap::iterator?j?=?refs->find(key);

if(j?!=?refs->end())?{

old_association?=?j->second;

refs->erase(j);

}

}

}

}

//?release?the?old?value?(outside?of?the?lock).

if(old_association.hasValue())?ReleaseValue()(old_association);

}

在看這段代碼前士败,我們需要先了解一下幾個(gè)數(shù)據(jù)結(jié)構(gòu)以及它們之間的關(guān)系:

AssociationsManager 是頂級(jí)的對(duì)象闯两,維護(hù)了一個(gè)從 spinlock_t 鎖到 AssociationsHashMap 哈希表的單例鍵值對(duì)映射;

AssociationsHashMap 是一個(gè)無(wú)序的哈希表谅将,維護(hù)了從對(duì)象地址到 ObjectAssociationMap 的映射生蚁;

ObjectAssociationMap 是一個(gè) C++ 中的 map ,維護(hù)了從 key 到 ObjcAssociation 的映射戏自,即關(guān)聯(lián)記錄;

ObjcAssociation 是一個(gè) C++ 的類伤锚,表示一個(gè)具體的關(guān)聯(lián)結(jié)構(gòu)擅笔,主要包括兩個(gè)實(shí)例變量,_policy 表示關(guān)聯(lián)策略屯援,_value 表示關(guān)聯(lián)對(duì)象猛们。

每一個(gè)對(duì)象地址對(duì)應(yīng)一個(gè) ObjectAssociationMap 對(duì)象,而一個(gè) ObjectAssociationMap 對(duì)象保存著這個(gè)對(duì)象的若干個(gè)關(guān)聯(lián)記錄狞洋。

弄清楚這些數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系后弯淘,再回過(guò)頭來(lái)看上面的代碼就不難了。我們發(fā)現(xiàn)吉懊,在蘋果的底層代碼中一般都會(huì)充斥著各種 if else 庐橙,可見(jiàn)寫(xiě)好 if else 后我們就距離成為高手不遠(yuǎn)了。開(kāi)個(gè)玩笑借嗽,我們來(lái)看下面的流程圖态鳖,一圖勝千言:

objc_getAssociatedObject

同樣的,我們也可以在 objc-references.mm 文件中找到 objc_getAssociatedObject 函數(shù)最終調(diào)用的函數(shù):

id?_object_get_associative_reference(id?object,?void?*key)?{

id?value?=?nil;

uintptr_t?policy?=?OBJC_ASSOCIATION_ASSIGN;

{

AssociationsManager?manager;

AssociationsHashMap?&associations(manager.associations());

disguised_ptr_t?disguised_object?=?DISGUISE(object);

AssociationsHashMap::iterator?i?=?associations.find(disguised_object);

if(i?!=?associations.end())?{

ObjectAssociationMap?*refs?=?i->second;

ObjectAssociationMap::iterator?j?=?refs->find(key);

if(j?!=?refs->end())?{

ObjcAssociation?&entry?=?j->second;

value?=?entry.value();

policy?=?entry.policy();

if(policy?&?OBJC_ASSOCIATION_GETTER_RETAIN)?((id(*)(id,?SEL))objc_msgSend)(value,?SEL_retain);

}

}

}

if(value?&&?(policy?&?OBJC_ASSOCIATION_GETTER_AUTORELEASE))?{

((id(*)(id,?SEL))objc_msgSend)(value,?SEL_autorelease);

}

returnvalue;

}

看懂了 objc_setAssociatedObject 函數(shù)后恶导,objc_getAssociatedObject 函數(shù)對(duì)我們來(lái)說(shuō)就是小菜一碟了浆竭。這個(gè)函數(shù)先根據(jù)對(duì)象地址在 AssociationsHashMap 中查找其對(duì)應(yīng)的 ObjectAssociationMap 對(duì)象,如果能找到則進(jìn)一步根據(jù) key 在 ObjectAssociationMap 對(duì)象中查找這個(gè) key 所對(duì)應(yīng)的關(guān)聯(lián)結(jié)構(gòu) ObjcAssociation ,如果能找到則返回 ObjcAssociation 對(duì)象的 value 值邦泄,否則返回 nil 删窒。

objc_removeAssociatedObjects

同理,我們也可以在 objc-references.mm 文件中找到 objc_removeAssociatedObjects 函數(shù)最終調(diào)用的函數(shù):

void?_object_remove_assocations(id?object)?{

vector<?ObjcAssociation,ObjcAllocator?>?elements;

{

AssociationsManager?manager;

AssociationsHashMap?&associations(manager.associations());

if(associations.size()?==?0)return;

disguised_ptr_t?disguised_object?=?DISGUISE(object);

AssociationsHashMap::iterator?i?=?associations.find(disguised_object);

if(i?!=?associations.end())?{

//?copy?all?of?the?associations?that?need?to?be?removed.

ObjectAssociationMap?*refs?=?i->second;

for(ObjectAssociationMap::iterator?j?=?refs->begin(),?end?=?refs->end();?j?!=?end;?++j)?{

elements.push_back(j->second);

}

//?remove?the?secondary?table.

deleterefs;

associations.erase(i);

}

}

//?the?calls?to?releaseValue()?happen?outside?of?the?lock.

for_each(elements.begin(),?elements.end(),?ReleaseValue());

}

這個(gè)函數(shù)負(fù)責(zé)移除一個(gè)對(duì)象的所有關(guān)聯(lián)對(duì)象顺囊,具體實(shí)現(xiàn)也是先根據(jù)對(duì)象的地址獲取其對(duì)應(yīng)的 ObjectAssociationMap 對(duì)象肌索,然后將所有的關(guān)聯(lián)結(jié)構(gòu)保存到一個(gè) vector 中,最終釋放 vector 中保存的所有關(guān)聯(lián)對(duì)象包蓝。根據(jù)前面的實(shí)驗(yàn)觀察到的情況驶社,在一個(gè)對(duì)象被釋放時(shí),也正是調(diào)用的這個(gè)函數(shù)來(lái)移除其所有的關(guān)聯(lián)對(duì)象测萎。

給類對(duì)象添加關(guān)聯(lián)對(duì)象

看完源代碼后亡电,我們知道對(duì)象地址與 AssociationsHashMap 哈希表是一一對(duì)應(yīng)的。那么我們可能就會(huì)思考這樣一個(gè)問(wèn)題硅瞧,是否可以給類對(duì)象添加關(guān)聯(lián)對(duì)象呢份乒?答案是肯定的。我們完全可以用同樣的方式給類對(duì)象添加關(guān)聯(lián)對(duì)象腕唧,只不過(guò)我們一般情況下不會(huì)這樣做或辖,因?yàn)楦鄷r(shí)候我們可以通過(guò) static 變量來(lái)實(shí)現(xiàn)類級(jí)別的變量。我在分類 ViewController+AssociatedObjects 中給 ViewController 類對(duì)象添加了一個(gè)關(guān)聯(lián)對(duì)象 associatedObject 枣接,讀者可以親自在 viewDidLoad 方法中調(diào)用一下以下兩個(gè)方法驗(yàn)證一下:

+?(NSString?*)associatedObject;

+?(void)setAssociatedObject:(NSString?*)associatedObject;

總結(jié)

讀到這里颂暇,相信你對(duì)開(kāi)篇的那三個(gè)問(wèn)題已經(jīng)有了一定的認(rèn)識(shí),下面我們?cè)偈崂硪幌拢?/p>

關(guān)聯(lián)對(duì)象與被關(guān)聯(lián)對(duì)象本身的存儲(chǔ)并沒(méi)有直接的關(guān)系但惶,它是存儲(chǔ)在單獨(dú)的哈希表中的耳鸯;

關(guān)聯(lián)對(duì)象的五種關(guān)聯(lián)策略與屬性的限定符非常類似,在絕大多數(shù)情況下膀曾,我們都會(huì)使用 OBJC_ASSOCIATION_RETAIN_NONATOMIC 的關(guān)聯(lián)策略县爬,這可以保證我們持有關(guān)聯(lián)對(duì)象;

關(guān)聯(lián)對(duì)象的釋放時(shí)機(jī)與移除時(shí)機(jī)并不總是一致添谊,比如實(shí)驗(yàn)中用關(guān)聯(lián)策略 OBJC_ASSOCIATION_ASSIGN 進(jìn)行關(guān)聯(lián)的對(duì)象财喳,很早就已經(jīng)被釋放了,但是并沒(méi)有被移除斩狱,而再使用這個(gè)關(guān)聯(lián)對(duì)象時(shí)就會(huì)造成 Crash 耳高。

在弄懂 Associated Objects 的實(shí)現(xiàn)原理后,可以幫助我們更好地使用它喊废,在出現(xiàn)問(wèn)題時(shí)也能盡快地定位問(wèn)題祝高,最后希望本文能夠?qū)δ阌兴鶐椭?/p>

參考鏈接

http://nshipster.com/associated-objects/

http://kingscocoa.com/tutorials/associated-objects/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市污筷,隨后出現(xiàn)的幾起案子工闺,更是在濱河造成了極大的恐慌乍赫,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件陆蟆,死亡現(xiàn)場(chǎng)離奇詭異雷厂,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)叠殷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門改鲫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人林束,你說(shuō)我怎么就攤上這事像棘。” “怎么了壶冒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵缕题,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我胖腾,道長(zhǎng)烟零,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任咸作,我火速辦了婚禮锨阿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘记罚。我一直安慰自己墅诡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布桐智。 她就那樣靜靜地躺著书斜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酵使。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天焙糟,我揣著相機(jī)與錄音口渔,去河邊找鬼。 笑死穿撮,一個(gè)胖子當(dāng)著我的面吹牛缺脉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播悦穿,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼攻礼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了栗柒?” 一聲冷哼從身側(cè)響起礁扮,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后太伊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體雇锡,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年僚焦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了锰提。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡芳悲,死狀恐怖立肘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情名扛,我是刑警寧澤谅年,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站罢洲,受9級(jí)特大地震影響踢故,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惹苗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一殿较、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧桩蓉,春花似錦淋纲、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至业汰,卻和暖如春伙窃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背样漆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工为障, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人放祟。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓鳍怨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親跪妥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鞋喇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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