Category的本質(zhì)<三>關(guān)聯(lián)對(duì)象

Category的本質(zhì)<一>
Category的本質(zhì)<二>load温眉,initialize方法
面試題:Category能否添加成員變量忠藤?如果可以掉冶,如何給Category添加成員變量?
我們首先創(chuàng)建一個(gè)類Person類繼承自NSObject糙及,給這個(gè)類聲明一個(gè)屬性name:

@property (nonatomic, strong)NSString *name;

我們聲明了這句話之后详幽,實(shí)際是做了三件事:

  • 1.聲明了一個(gè)成員變量_name。
NSString *_name;
  • 2.聲明了set和get方法:
- (void)setName:(NSString *)name;
- (NSString *)name;
  • 3.在.m文件中實(shí)現(xiàn)set和get方法:
- (void)setName:(NSString *)name{
    
    _name = name;
}

- (NSString *)name{
    
    return _name;
}

以上是給一個(gè)類添加屬性。下面給一個(gè)分類添加屬性:
我們創(chuàng)建一個(gè)Person類的分類Test1唇聘,然后給這個(gè)分類添加一個(gè)height屬性:

@property (nonatomic, assign)int height;

這樣只會(huì)申明set和get方法版姑,而不會(huì)申明成員變量和實(shí)現(xiàn)set,get方法:

- (void)setHeight:(int)height;
- (int)height;

既然系統(tǒng)沒(méi)有幫我們聲明成員變量和實(shí)現(xiàn)set和get方法迟郎,那么我們能不能自己去聲明一下呢剥险?我們嘗試一下:

F971DF04-F0BD-4B7C-8108-376F0DECDF3C.png

出現(xiàn)了報(bào)錯(cuò)Instance variables may not be placed in categories,意思就是成員變量不能聲明在分類中宪肖。所以我們得出結(jié)論表制,分類中不能添加成員變量。

我們從分類的結(jié)構(gòu)的角度來(lái)考慮一下分類中為什么不能添加成員變量:
image.png

通過(guò)分類的底層結(jié)構(gòu)我們可以看到控乾,分類中可以存放實(shí)例方法么介,類方法,協(xié)議蜕衡,屬性夭拌,但是沒(méi)有存放成員變量的地方。
既然分類中不能添加成員變量衷咽,那么我們給分類添加屬性時(shí),它的功能不是完整的蒜绽,比如說(shuō)我們分別給Person類的name和height這兩個(gè)屬性賦值镶骗,然后打印讀出這兩個(gè)屬性:

Person *person = [[Person alloc] init];
person.name = @"dongdong";
person.height = 180;
    
NSLog(@"name: %@, height : %d", person.name, person.height);

程序崩潰了,崩潰原因是:-[Person setHeight:]: unrecognized selector sent to instance 0x60400020a0d0躲雅,意思就是給這個(gè)person對(duì)象發(fā)送了沒(méi)有實(shí)現(xiàn)的消息:setHeight:,這應(yīng)該是在我們的預(yù)料之中鼎姊,為什么呢?因?yàn)槲覀冊(cè)诜诸愔新暶?code>height這個(gè)屬性的時(shí)候相赁,不像在類中聲明屬性一樣相寇,系統(tǒng)只會(huì)聲明set和get方法,而不會(huì)在.m中去實(shí)現(xiàn)set和get方法钮科,因此導(dǎo)致了程序崩潰唤衫。因此我們?cè)诜诸惖?m文件中去實(shí)現(xiàn)set和get方法:

//Person+Test1.m文件
- (void)setHeight:(int)height{
    
}

- (int)height{
    
    return 0;
}

再次運(yùn)行代碼,這次程序不崩潰了绵脯,打印結(jié)果是:

Category[9030:308848] name: dongdong, height : 0

我們看到name屬性賦值成功了佳励,而height屬性顯然沒(méi)有賦值成功。

person.height = 180;

這句代碼顯然是調(diào)用了set方法蛆挫,但是在分類中的set方法什么也沒(méi)有實(shí)現(xiàn)赃承,沒(méi)有存儲(chǔ)下這個(gè)設(shè)置的值180。

person.height

實(shí)則是調(diào)用了get方法悴侵,由于不能保存?zhèn)鬟f過(guò)來(lái)的height值瞧剖,所以上面的代碼中我們返回固定值0。
而name屬性能夠賦值和讀取成功,是因?yàn)樵谄鋝et方法中用_name這個(gè)成員變量保存的賦的值:

- (void)setName:(NSString *)name{
    
    _name = name;
}

在其get方法中利用_name成員變量返回存儲(chǔ)的值:

- (NSString *)name{
    
    return _name;
}

所以如果我們?cè)诜诸惖?m文件中保存?zhèn)鬟f過(guò)來(lái)的值抓于,然后在取值的時(shí)候返回存儲(chǔ)的值做粤,那么應(yīng)該也能實(shí)現(xiàn)屬性的完整功能。

方法一 全局變量

第一種方法是使用全局變量來(lái)存儲(chǔ)傳遞進(jìn)來(lái)的值:

int height_;

- (void)setHeight:(int)height{
    height_ = height;
}

- (int)height{
    
    return height_;
}

然后我們運(yùn)行一下程序:

Category[9497:328996] name: dongdong, height : 180

這次好像是賦值成功了毡咏,返回也對(duì)驮宴。我們?cè)侔裩eight改成190試試:

Category[9533:330381] name: dongdong, height : 190

這次打印的也是對(duì)的,那么這樣是不是就真的可以完全實(shí)現(xiàn)屬性的功能呢呕缭?
問(wèn)題在于堵泽,height_是全局變量,所有的對(duì)象共用這一個(gè)全局變量恢总,如果有個(gè)對(duì)象的height值變了迎罗,其他的對(duì)象的height值也會(huì)跟著改變,也是不符合我們的需求的片仿,我們可以測(cè)試一下:

Person *person1 = [[Person alloc] init];
person1.height = 180;
Person *person2 = [[Person alloc] init];
person2.height = 190;
    
 NSLog(@"person1: %d, person2 : %d", person1.height, person2.height);

打印結(jié)果:

Category[9648:335004] person1: 190, person2 : 190

所以這種方法就被pass掉了纹安。

方法二 字典

第一種方法全局變量失敗的原因就是不能做到每個(gè)對(duì)象和自己的height值一一對(duì)應(yīng)。這就讓我們想到了一個(gè)數(shù)據(jù)結(jié)構(gòu)-字典砂豌。假如我們通過(guò)鍵值對(duì)的形式存放height值厢岂,這樣是否可以呢?我們使用person對(duì)象指向的地址作為鍵阳距,將height值作為值存儲(chǔ)在字典中:

NSMutableDictionary *heights_;
//由于load方法只初始化一次塔粒,所以我們可以在這個(gè)方法里做一些初始化操作
+ (void)load{
    
    heights_ = [NSMutableDictionary dictionary];
}

- (void)setHeight:(int)height{
    NSString *key = [NSString stringWithFormat:@"%p", self];
    heights_[key] = @(height);
}

- (int)height{
    
    NSString *key = [NSString stringWithFormat:@"%p", self];
    return [heights_[key] intValue];
}
Person *person1 = [[Person alloc] init];
person1.height = 180;
Person *person2 = [[Person alloc] init];
person2.height = 190;
    
NSLog(@"person1: %d, person2 : %d", person1.height, person2.height);

打印結(jié)果:

Category[10166:350395] person1: 180, person2 : 190

所以采用字典這種方式是完全可行的。

使用字典存在的問(wèn)題:
  • 1.非線程安全
    由于這個(gè)字典是全局的筐摘,所有的對(duì)象的height屬性值都是存儲(chǔ)在這個(gè)全局字典里面卒茬,當(dāng)不同的對(duì)象在不同的線程同時(shí)訪問(wèn)這個(gè)全局字典時(shí),這個(gè)時(shí)候就容易產(chǎn)生線程安全問(wèn)題咖熟,需要去加線程鎖圃酵,有些復(fù)雜。
  • 2.需要?jiǎng)?chuàng)建多個(gè)全局字典
    剛才已經(jīng)看到了馍管,我們需要為分類中的每一個(gè)屬性值創(chuàng)建一個(gè)全局字典郭赐,這是非常麻煩又復(fù)雜的事。

方法三 關(guān)聯(lián)對(duì)象

關(guān)聯(lián)對(duì)象使用的是runtime的API:

/****
//這個(gè)方法是在set方法中使用确沸,目的是把傳遞進(jìn)來(lái)的value值和object這個(gè)對(duì)象關(guān)聯(lián)起來(lái)
@object:這個(gè)參數(shù)是要關(guān)聯(lián)的對(duì)象
@key:在這里設(shè)置了key值堪置,那么在get方法里面就可以根據(jù)這個(gè)key取得值
@value:傳遞進(jìn)來(lái)的值
@policy:它是個(gè)一個(gè)枚舉值,用來(lái)修飾value
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,          
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, 
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
    OBJC_ASSOCIATION_RETAIN = 01401,      
    OBJC_ASSOCIATION_COPY = 01403         
};
***/
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                         id _Nullable value, objc_AssociationPolicy policy)
/***
//這個(gè)方法是在get方法中使用张惹,獲得關(guān)聯(lián)對(duì)象的值舀锨。
@object:關(guān)聯(lián)的對(duì)象
@key:set方法中設(shè)置的key值
***/
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)

我們?cè)俳oPerson類的分類聲明一個(gè)屬性:

@property (nonatomic, copy)NSString *sex;

然后我們使用關(guān)聯(lián)對(duì)象的方法給sex這個(gè)屬性賦值和取值:

//由于key的類型是`void *`類型,也就是一個(gè)指針類型宛逗,所以這里聲明了一個(gè)指針類型的sexKey
const void *sexKey;

- (void)setSex:(NSString *)sex{
    
    objc_setAssociatedObject(self, sexKey, sex, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)sex{
    
    return objc_getAssociatedObject(self, sexKey);
}
Person *person1 = [[Person alloc] init];
person1.sex = @"man";
Person *person2 = [[Person alloc] init];
person2.sex = @"women";
    
NSLog(@"person1: %@, person2 : %@", person1.sex, person2.sex);

打印結(jié)果:

Category[11243:396207] person1: man, person2 : women

我們發(fā)現(xiàn)打印結(jié)果是正確的坎匿。
但是這里存在一個(gè)問(wèn)題就是我們?cè)O(shè)置的key沒(méi)有賦值,也即是sexKey相當(dāng)于NULL,假如我們?cè)俳oheight屬性設(shè)置一個(gè)key為heightKey替蔬,那么這個(gè)heightKey也是NULL告私,那么在get方法中通過(guò)key值來(lái)取得值時(shí),由于屬性的key都是一樣的承桥,所以就很容易出錯(cuò)驻粟。

  • 方法一
    因此我們需要給這個(gè)sexKey賦值一個(gè)獨(dú)一無(wú)二的值:
const void *sexKey = &sexKey;

這句話就是直接將sexKey這個(gè)指針的地址值賦給自己。對(duì)于height:

const void *heightKey = &heightKey;

由于這兩個(gè)指針?lè)诸愒诓煌膬?nèi)存地址中凶异,所以heightKey和sexKey可以保證是不相同的蜀撑,這樣就能在get方法中取出正確的值。

  • 方法二
    上面這種方式實(shí)在是非常啰嗦又累贅剩彬,我們要聲明指針酷麦,初始化指針,下面介紹一種更簡(jiǎn)單的方法:
- (void)setSex:(NSString *)sex{
    
    objc_setAssociatedObject(self, @"sex", sex, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)sex{
    
    return objc_getAssociatedObject(self, @"sex");
}

我們直接把@"sex"這個(gè)字符串傳進(jìn)去作為key喉恋,這樣就不用聲明指針又初始化了沃饶。有人就有疑問(wèn)了,這里的key明明要求是指針類型的轻黑,我們傳進(jìn)一個(gè)字符串可以嗎糊肤?我們分析一下下面這句代碼:

NSString *name = @"dongdong";

這里name變量是一個(gè)指針變量。那么我們?yōu)槭裁茨苡靡粋€(gè)字符串去初始化一個(gè)指針變量呢氓鄙?原因就是這里傳進(jìn)去的是@"dongdong"這個(gè)字符串的地址轩褐。這樣我們就能明白,上面@"sex"其實(shí)傳進(jìn)去的也是這個(gè)字符串的地址玖详。
為了防止誤寫(xiě),我們還可以把字符串抽成宏:

#define SEX @"sex"

- (void)setSex:(NSString *)sex{
    
    objc_setAssociatedObject(self, SEX, sex, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)sex{
    
    return objc_getAssociatedObject(self, SEX);
}

方法三
第二種方法已經(jīng)非常簡(jiǎn)便了勤讽,但是為了方便準(zhǔn)確我們還要把字符串抽成宏蟋座。有沒(méi)有更加簡(jiǎn)便的方法呢?我們可以嘗試傳進(jìn)一個(gè)方法的地址作為key脚牍,比如說(shuō)set或get方法:

- (void)setSex:(NSString *)sex{
    
    objc_setAssociatedObject(self, @selector(sex), sex, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)sex{
    
    return objc_getAssociatedObject(self, @selector(sex));
}

這里傳進(jìn)去的key是@selector(sex)向臀,也就是sex這個(gè)get方法的地址。當(dāng)然我們也可以傳進(jìn)set方法的地址作為key诸狭。最后我們還可以更進(jìn)一步的簡(jiǎn)化:

- (void)setSex:(NSString *)sex{
    
    objc_setAssociatedObject(self, @selector(sex), sex, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)sex{
    
    return objc_getAssociatedObject(self, _cmd);
}

這里在get方法里把@selector(sex)換成了_cmd,這是因?yàn)槲覀兪褂玫膋ey是sex這個(gè)方法的地址券膀,在這個(gè)方法內(nèi)部,我們可以直接使用_cmd獲取本方法驯遇。那這樣就非常方便簡(jiǎn)潔了芹彬。

關(guān)聯(lián)對(duì)象的原理

set方法

objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)方法
我們直接去runtime的源碼中去查看關(guān)聯(lián)對(duì)象的具體實(shí)現(xiàn),直接搜索objc_setA,

24E2331B-598F-4F7B-A012-B03564C99C88.png

  • 1.選擇objc-runtime.mm這個(gè)文件中的實(shí)現(xiàn):
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}
  • 2.點(diǎn)進(jìn)_object_set_associative_reference(object, (void *)key, value, policy);這個(gè)真實(shí)的實(shí)現(xiàn)函數(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 = new ObjectAssociationMap;
                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ù)的實(shí)現(xiàn)看起來(lái)非常復(fù)雜叉庐,都是C++的語(yǔ)法舒帮,對(duì)于不了解C++的人來(lái)說(shuō)非常困難,不過(guò)沒(méi)關(guān)系,即便我們看不懂上面的代碼玩郊,通過(guò)下面的分析肢执,我們也能明白關(guān)聯(lián)對(duì)象的原理:
實(shí)現(xiàn)關(guān)聯(lián)對(duì)象技術(shù)的核心對(duì)象有:

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjectAssociation
    這里面經(jīng)常出現(xiàn)Map這個(gè)東西,這其實(shí)和我們Objective-c中的字典是一樣的译红,我們可以把它當(dāng)字典來(lái)看待预茄。在第二種方法里面我們是用字典去實(shí)現(xiàn)的,這里又出現(xiàn)了和字典相似的結(jié)構(gòu)侦厚,那它們的實(shí)現(xiàn)會(huì)不會(huì)相似呢耻陕?
    在上面的一大段源碼中,我們?cè)陂_(kāi)頭的位置找到這一句:
AssociationsManager manager;

我們點(diǎn)進(jìn)AssociationsManager查看其結(jié)構(gòu):

759A21C4-A407-490A-9F90-AE740E138B4D.png

前面講了Map類型是字典假夺,那么什么是key淮蜈,什么是value呢?然后我們繼續(xù)點(diǎn)進(jìn)AssociationsHashMap
D72D1231-FFD8-40ED-8CE0-E9D2146A4ECD.png

我們前面也講了已卷,ObjectAssociationMap這個(gè)結(jié)構(gòu)也是字典梧田,那么這個(gè)字典里面裝的是什么呢?我們點(diǎn)進(jìn)去看看:
893E11B0-3813-441F-9833-5839CC45FD7C.png

那這個(gè)ObjcAssociation又是什么東西呢侧蘸?我們進(jìn)去看看:
35314FE8-4390-4BEA-9619-6FF40D3FB9F2.png

總結(jié)一下上面四個(gè)核心對(duì)象的結(jié)構(gòu):
9C8C4B75-E1B0-425C-8E91-C7B833181741.png

下面這張圖總結(jié)的是這四個(gè)核心對(duì)象之間的聯(lián)系:
D8080A40-5585-474E-A224-B5BBE573DB22.png

那么問(wèn)題來(lái)了裁眯,objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)中的四個(gè)參數(shù)分別對(duì)應(yīng)上面結(jié)構(gòu)中的哪個(gè)結(jié)構(gòu)呢?
下圖就展示了它們的對(duì)應(yīng)關(guān)系:
0423B811-3976-46A5-95CC-FCDFDB9FE236.png

拿我們之前寫(xiě)的作為例子:

objc_setAssociatedObject(self, @selector(sex), sex, OBJC_ASSOCIATION_COPY_NONATOMIC);

這句代碼中讳癌,self也就是person對(duì)象被賦給了AssociationHashMap的key穿稳,而@selector(sex)的地址被賦給了AssociationMap的key,策略OBJC_ASSOCIATION_COPY_NONATOMIC被賦值給了ObjectAssociation的policy晌坤,傳遞進(jìn)來(lái)的值sex被賦值給了ObjectAssociation的value逢艘。
這種設(shè)計(jì)的巧妙之處就在于:
當(dāng)一個(gè)person對(duì)象不光有一個(gè)屬性值要關(guān)聯(lián)時(shí),比如我們要關(guān)聯(lián)height和sex這兩個(gè)屬性時(shí)骤菠,我們以person對(duì)象作為key它改,然后值是AssociationMap這個(gè)字典類型,在這個(gè)字典類型中商乎,分別使用@selector(sex)@selector(height)作為key央拖,然后分別利用sex屬性的policy和傳遞進(jìn)來(lái)的value和height屬性的policy和傳遞進(jìn)來(lái)的value生成ObjectAssociation作為value。而如果有多個(gè)person對(duì)象需要關(guān)聯(lián)時(shí)鹉戚,我們只需要在AssociationHashMap中創(chuàng)造更多的鍵值對(duì)就可以解決這個(gè)問(wèn)題鲜戒。
通過(guò)這個(gè)過(guò)程我們也能明白:
關(guān)聯(lián)對(duì)象的值它不是存儲(chǔ)在自己的實(shí)例對(duì)象的結(jié)構(gòu)中,而是維護(hù)了一個(gè)全局的結(jié)構(gòu)AssociationManager

get方法

objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)方法
經(jīng)過(guò)了上面的分析抹凳,基本上就對(duì)set方法的原理比較清楚了遏餐,下面我們直接看一下get方法的源碼:

  • 1.在runtime的源碼中找到這個(gè)函數(shù):
id objc_getAssociatedObject(id object, const void *key) {
    return _object_get_associative_reference(object, (void *)key);
}
  • 2.點(diǎn)進(jìn)_object_get_associative_reference(object, (void *)key);
    3EFE0EEF-7D66-44FF-AC39-55386E6AE3BB.png

回答面試題

Category能否添加成員變量?如果可以赢底,如何給Category添加成員變量境输?
答:不能直接給Category添加成員變量蔗牡,但是可以間接實(shí)現(xiàn)Category有成員變量的效果。我們可以使用runtime的API嗅剖,objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy)objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)這兩個(gè)來(lái)實(shí)現(xiàn)辩越。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市信粮,隨后出現(xiàn)的幾起案子黔攒,更是在濱河造成了極大的恐慌,老刑警劉巖强缘,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件督惰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡旅掂,警方通過(guò)查閱死者的電腦和手機(jī)赏胚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)商虐,“玉大人觉阅,你說(shuō)我怎么就攤上這事∶爻担” “怎么了典勇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)叮趴。 經(jīng)常有香客問(wèn)我割笙,道長(zhǎng),這世上最難降的妖魔是什么眯亦? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任伤溉,我火速辦了婚禮,結(jié)果婚禮上妻率,老公的妹妹穿的比我還像新娘乱顾。我一直安慰自己,他們只是感情好舌涨,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著扔字,像睡著了一般囊嘉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上革为,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天扭粱,我揣著相機(jī)與錄音,去河邊找鬼震檩。 笑死琢蛤,一個(gè)胖子當(dāng)著我的面吹牛蜓堕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播博其,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼套才,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了慕淡?” 一聲冷哼從身側(cè)響起背伴,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎峰髓,沒(méi)想到半個(gè)月后傻寂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡携兵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年疾掰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徐紧。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡静檬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出浪汪,到底是詐尸還是另有隱情巴柿,我是刑警寧澤,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布死遭,位于F島的核電站广恢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏呀潭。R本人自食惡果不足惜钉迷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钠署。 院中可真熱鬧糠聪,春花似錦、人聲如沸谐鼎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)狸棍。三九已至身害,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間草戈,已是汗流浹背塌鸯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留唐片,地道東北人丙猬。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓涨颜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親茧球。 傳聞我的和親對(duì)象是個(gè)殘疾皇子庭瑰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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