基于FMDB构建网络缓存

核心思想:存储的Key 经过MD5加密,将NSDictionary;NSArray;NSString;NSData转换为NSString 存储为text格式,同时将数据类型写入数据表;读取时,根据储存的数据类型还原即可;

上代码:

先来看一眼API:

/**
 *  更新缓存数据,则在缓存有效期过后更新缓存;(如果没数据则创建,如果有数据则更新)
 *
 *  @param urlString    以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param parameter    parameter
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)updataCacheWithUrl:(NSString *)urlString parameter:(id) parameter cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;
/**
 *  强制更新缓存数据;(如果没数据则创建,如果有数据则更新)
 *
 *  @param urlString    以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param parameter    parameter
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)forceUpdataCacheWithUrl:(NSString *)urlString parameter:(id) parameter cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;

/**
 *  更新缓存数据,则在缓存有效期过后更新缓存;(如果没数据则创建,如果有数据则更新)
 *
 *  @param key          以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)updataCacheWithKey:(NSString *)key cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;

/**
 *  强制更新缓存数据;(如果没数据则创建,如果有数据则更新)
 *
 *  @param key          以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)forceUpdataCacheWithKey:(NSString *)key cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;

/**
 *  加载缓存数据
 *
 *  @param urlString url
 *  @param parameter 参数
 *
 *  @return 缓存数据
 */
-(id)loadCacheWithUrl:(NSString *)urlString parameter:(id) parameter;

/**
 *  加载缓存数据
 *
 *  @param key 缓存数据库的 key: 内部以KeyMD5作为Key
 *
 *  @return 缓存数据
 */
-(id)loadCacheWithKey:(NSString *)key;

/**
 *  清空缓存数据表
 */
-(void)clearDatabaseTable;

核心代码片段:

NSDictionary;NSArray;NSString;NSData转换为NSString,便于写入数据表


- (NSString *)stringWithData:(id )data{
    if ([data isKindOfClass:[NSDictionary class]]) { // 数组/字典
        NSData      *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
        return  [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }else if ([data isKindOfClass:[NSArray class]]){
        NSArray *array = data;
        return [array componentsJoinedByString:@","];
    }else if([data isKindOfClass:[NSData class]]){
        return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }else if([data isKindOfClass:[NSString class]]){
        return data;
    }else{
        NSAssert(NO, @"数据类型不支持,目前只支持: NSDictionary;NSArray;NSString;NSData");
    }
    return nil;
}

数据读取后还原过程

- (id)dataWithString:(NSString *)string classType:(NSNumber *)classType{

    NSInteger  type = [classType integerValue];
    switch (type) {
        case ClassTypeDictionary:{
            NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
            return  [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
        }break;
        case ClassTypeArray:
            return [string componentsSeparatedByString:@","];
            break;
        case ClassTypeData:
            return [string dataUsingEncoding:NSUTF8StringEncoding];
            break;
        case ClassTypeString:
            return string;
            break;
        default: // ClassTypeOther
            return nil;
            break;
    }
}

以下为FMDB 基本使用方法:

创建数据库

-(void)creatScarchRecodeTable{
    
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains
                              (NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *path = [documentPath stringByAppendingPathComponent:@"DTDateCache.db"];
    _database = [FMDatabase databaseWithPath:path];
      NSLog(@"%@",path);
    if ([_database open]) {
        @try {
          [_database executeUpdate:[NSString stringWithFormat:@"create table if not exists DTCacheTable  (id integer PRIMARY KEY AUTOINCREMENT, keyName text, creatTime text, validTime text,cacheData text,classType integer)"]];
          
        } @catch (NSException *exception) {
            NSAssert(NO, exception.description);
        } @finally {
        }  
    }
}

插入数据

-(void)insertDataString:(NSString  *)data   withKey:(NSString *)key  classType:(NSNumber *)classtype{
    if (/* DISABLES CODE */ (NO)) {
        FMResultSet * result = [_database executeQuery:@"SELECT * FROM table order by time desc LIMIT 1"];
        [_database executeUpdate:[NSString stringWithFormat:@"DELETE FROM DTCacheTable WHERE name='%@'",[result stringForColumn:@"recode"]]];
    }
    [self creatScarchRecodeTable];
    NSString *sqlite = [NSString stringWithFormat:@"INSERT INTO DTCacheTable (keyName,creatTime,validTime,cacheData,classType) VALUES (?,?,?,?,?)"];
    NSString *creatTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000)];
    NSString *validTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000) + _cacheValidTime];
    BOOL success = [_database executeUpdate:sqlite, key,creatTime,validTime,data,classtype];
    NSString *des = [NSString stringWithFormat:@"%@",[_database lastErrorMessage]];
//    [_database close];
    NSAssert(success, des);
}

更新数据

-(void)updataDataWithKey:(NSString *)key cacheData:(NSString *)cacheData classType:(NSNumber *)classtype{
    @try {
        [self creatScarchRecodeTable];
        NSString *creatTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000)];
        NSString *validTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000) + _cacheValidTime];

        NSString *update = [NSString stringWithFormat:@"UPDATE DTCacheTable SET creatTime = ?, validTime = ?, cacheData = ?, classType = ?   WHERE keyName = ? "];
        [_database executeUpdate:update,creatTime,validTime,cacheData,classtype,key];
//        [_database close];
    } @catch (NSException *exception) {
        NSAssert(NO, exception.description);
    } @finally {

    }
}

查询数据

-(NSDictionary *)selectDataFromTableWithKey:(NSString *)key{
    
    FMResultSet * result = [_database executeQuery:[NSString stringWithFormat:@"SELECT * FROM DTCacheTable"]];
    while ([result next]) {
        if ([key isEqualToString: [result stringForColumn:@"keyName"]]) {
            NSNumber *classType = [NSNumber numberWithInt:[result intForColumn:@"classType"]];
           NSString *content = [result stringForColumn:@"cacheData"];
          return [NSDictionary dictionaryWithObjectsAndKeys:classType,@"classType",content,@"content", nil];
        }
    }
//    [_database close];
    return nil;
}

以下是完整代码:

#import <Foundation/Foundation.h>

@interface DTSqliteManager : NSObject

singleTon_h(DTSqliteManager)

/**
 *  更新缓存数据,则在缓存有效期过后更新缓存;(如果没数据则创建,如果有数据则更新)
 *
 *  @param urlString    以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param parameter    parameter
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)updataCacheWithUrl:(NSString *)urlString parameter:(id) parameter cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;
/**
 *  强制更新缓存数据;(如果没数据则创建,如果有数据则更新)
 *
 *  @param urlString    以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param parameter    parameter
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)forceUpdataCacheWithUrl:(NSString *)urlString parameter:(id) parameter cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;

/**
 *  更新缓存数据,则在缓存有效期过后更新缓存;(如果没数据则创建,如果有数据则更新)
 *
 *  @param key          以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)updataCacheWithKey:(NSString *)key cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;

/**
 *  强制更新缓存数据;(如果没数据则创建,如果有数据则更新)
 *
 *  @param key          以url的host,soureUrl,参数列表,拼接之后MD5作为Key
 *  @param cacheData    cacheData
 *  @param cacheSeconds 缓存有效期
 */
-(void)forceUpdataCacheWithKey:(NSString *)key cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;

/**
 *  加载缓存数据
 *
 *  @param urlString url
 *  @param parameter 参数
 *
 *  @return 缓存数据
 */
-(id)loadCacheWithUrl:(NSString *)urlString parameter:(id) parameter;

/**
 *  加载缓存数据
 *
 *  @param key 缓存数据库的 key: 内部以KeyMD5作为Key
 *
 *  @return 缓存数据
 */
-(id)loadCacheWithKey:(NSString *)key;

/**
 *  清空缓存数据表
 */
-(void)clearDatabaseTable;
@end
#import "DTSqliteManager.h"
#import <FMDB/FMDB.h>
#import <sqlite3.h>
#import <CommonCrypto/CommonDigest.h>
//#import "sys/utsname.h"
typedef NS_ENUM(NSUInteger, ClassType) {
    ClassTypeDictionary = 1 << 0,
    ClassTypeArray      = 1 << 1,
    ClassTypeString     = 1 << 2,
    ClassTypeData       = 1 << 3,
    ClassTypeOther      = 1 << 4
};

@interface DTSqliteManager()
@end

@implementation DTSqliteManager{
    FMDatabase *_database;
    NSInteger   _cacheValidTime;
}

singleTon_m(DTSqliteManager)

#pragma mark - 创建数据库和表
-(void)creatScarchRecodeTable{
    
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains
                              (NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString *path = [documentPath stringByAppendingPathComponent:@"DTDateCache.db"];
    _database = [FMDatabase databaseWithPath:path];
      NSLog(@"%@",path);
    if ([_database open]) {
        @try {
          [_database executeUpdate:[NSString stringWithFormat:@"create table if not exists DTCacheTable  (id integer PRIMARY KEY AUTOINCREMENT, keyName text, creatTime text, validTime text,cacheData text,classType integer)"]];
          
        } @catch (NSException *exception) {
            NSAssert(NO, exception.description);
        } @finally {
        }
        
    }
}

#pragma mark - 缓存数据库-插入数据
-(void)insertDataString:(NSString  *)data   withKey:(NSString *)key  classType:(NSNumber *)classtype{
    if (/* DISABLES CODE */ (NO)) {
        FMResultSet * result = [_database executeQuery:@"SELECT * FROM table order by time desc LIMIT 1"];
        [_database executeUpdate:[NSString stringWithFormat:@"DELETE FROM DTCacheTable WHERE name='%@'",[result stringForColumn:@"recode"]]];
    }
    [self creatScarchRecodeTable];
    NSString *sqlite = [NSString stringWithFormat:@"INSERT INTO DTCacheTable (keyName,creatTime,validTime,cacheData,classType) VALUES (?,?,?,?,?)"];
    NSString *creatTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000)];
    NSString *validTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000) + _cacheValidTime];
    BOOL success = [_database executeUpdate:sqlite, key,creatTime,validTime,data,classtype];
    NSString *des = [NSString stringWithFormat:@"%@",[_database lastErrorMessage]];
//    [_database close];
    NSAssert(success, des);
}

-(void)updataDataWithKey:(NSString *)key cacheData:(NSString *)cacheData classType:(NSNumber *)classtype{
    @try {
        [self creatScarchRecodeTable];
        NSString *creatTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000)];
        NSString *validTime = [NSString stringWithFormat:@"%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000) + _cacheValidTime];

        NSString *update = [NSString stringWithFormat:@"UPDATE DTCacheTable SET creatTime = ?, validTime = ?, cacheData = ?, classType = ?   WHERE keyName = ? "];
        [_database executeUpdate:update,creatTime,validTime,cacheData,classtype,key];
//        [_database close];
    } @catch (NSException *exception) {
        NSAssert(NO, exception.description);
    } @finally {

    }
}

#pragma mark - 缓存数据库-查询数据
-(NSDictionary *)selectDataFromTableWithKey:(NSString *)key{
    
    FMResultSet * result = [_database executeQuery:[NSString stringWithFormat:@"SELECT * FROM DTCacheTable"]];
    while ([result next]) {
        if ([key isEqualToString: [result stringForColumn:@"keyName"]]) {
            NSNumber *classType = [NSNumber numberWithInt:[result intForColumn:@"classType"]];
           NSString *content = [result stringForColumn:@"cacheData"];
          return [NSDictionary dictionaryWithObjectsAndKeys:classType,@"classType",content,@"content", nil];
        }
    }
//    [_database close];
    return nil;
}

#pragma mark - 清空缓存数据库
-(void)clearDatabaseTable{
    
    BOOL success = [_database executeUpdate:[NSString stringWithFormat:@"DELETE FROM DTCacheTable"]];
    NSString *des = [NSString stringWithFormat:@"%@",[_database lastErrorMessage]];
    NSAssert(success, des);
}

#pragma mark - 数据库表名
//-(NSString *)getDatabaseTableName{
//    return [@"DTCache" dt_Md5Str];
//}



-(void)updataCacheWithUrl:(NSString *)urlString parameter:(id) parameter cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds forceUpdata:(BOOL)force{
    _cacheValidTime = cacheSeconds;
    NSString * key_MD5 =[self stringToMD5String:[NSString stringWithFormat:@"%@%@",urlString,parameter]] ;
    //根据缓存是否存在,执行更新还是插入;
    if ([self hasCacheWiWithKey:key_MD5]) {
        if (force) {
            // 更新
             [self updataDataWithKey:key_MD5 cacheData: [self stringWithData:cacheData] classType:[self classTypeWithData:cacheData]];
        }else{
            if ([self cacheTimeOutWithKey:key_MD5]) {
                [self updataDataWithKey:key_MD5 cacheData: [self stringWithData:cacheData] classType:[self classTypeWithData:cacheData]];
            }
        }
    }else{
        // 插入
        [self insertDataString:[self stringWithData:cacheData] withKey:key_MD5 classType:[self classTypeWithData:cacheData]];
    }

}

-(void)updataCacheWithUrl:(NSString *)urlString parameter:(id) parameter cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;{
    [self updataCacheWithUrl:urlString parameter:parameter cacheData:cacheData cacheValidTime:cacheSeconds forceUpdata:NO];
}
-(void)forceUpdataCacheWithUrl:(NSString *)urlString parameter:(id) parameter cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;{
    [self updataCacheWithUrl:urlString parameter:parameter cacheData:cacheData cacheValidTime:cacheSeconds forceUpdata:NO];
}


-(BOOL)hasCacheWiWithKey:(NSString *)key{
    return  [self selectDataFromTableWithKey:key] ? YES:NO;
}

-(BOOL)cacheTimeOutWithKey:(NSString *)key{
    FMResultSet * result = [_database executeQuery:[NSString stringWithFormat:@"SELECT * FROM DTCacheTable"]];
    while ([result next]) {
        if ([key isEqualToString:[result stringForColumn:@"keyName"]]) {
            NSLog(@"+++++%f",[[result stringForColumn:@"validTime"]  doubleValue]);
            NSLog(@"缓存有效期%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000));
            NSLog(@"当前时间:%ld",(long)(kCFAbsoluteTimeIntervalSince1970 / 1000));
            return  [[result stringForColumn:@"validTime"]  intValue] > (int)(kCFAbsoluteTimeIntervalSince1970 / 1000) ? NO:YES;
        }
    }
    return YES;
}



-(void)updataCacheWithKey:(NSString *)key cacheData:(id)cacheData cacheValidTime:(NSInteger)cacheSeconds forceUpdata:(BOOL)force{
    NSString*  key_MD5 = [self stringToMD5String:key];
    _cacheValidTime = cacheSeconds;
    //根据缓存是否存在,执行更新还是插入;
    if ([self hasCacheWiWithKey:key_MD5]) {
        if (force) {
            // 更新
            [self updataDataWithKey:key_MD5 cacheData: [self stringWithData:cacheData] classType:[self classTypeWithData:cacheData]];

        }else{
            if ([self cacheTimeOutWithKey:key_MD5]) {
                // 更新
                [self updataDataWithKey:key_MD5 cacheData: [self stringWithData:cacheData] classType:[self classTypeWithData:cacheData]];
            }
        }
    }else{
        // 插入
        [self insertDataString:[self stringWithData:cacheData] withKey:key_MD5 classType:[self classTypeWithData:cacheData]];
    }
}


-(void)updataCacheWithKey:(NSString *)key cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;{
    [self updataCacheWithKey:key cacheData:cacheData cacheValidTime:cacheSeconds forceUpdata:NO];
}
-(void)forceUpdataCacheWithKey:(NSString *)key cacheData:(id) cacheData cacheValidTime:(NSInteger)cacheSeconds;{
    [self updataCacheWithKey:key cacheData:cacheData cacheValidTime:cacheSeconds forceUpdata:YES];
}

-(id)loadCacheWithUrl:(NSString *)urlString parameter:(id) parameter;{
    NSString *key_MD5 = [self stringToMD5String:[NSString stringWithFormat:@"%@%@",urlString,parameter]];
    if ([self hasCacheWiWithKey:key_MD5]) {
        NSDictionary *diction = [self selectDataFromTableWithKey:key_MD5];
        return [self  dataWithString:[diction objectForKey:@"content"] classType:[diction objectForKey:@"classType"]];
    }
    return nil;
}

-(id)loadCacheWithKey:(NSString *)key{
    NSString *key_MD5 = [self stringToMD5String:key];
    if ([self hasCacheWiWithKey:key_MD5]) {
        NSDictionary *diction = [self selectDataFromTableWithKey:key_MD5];
        return [self  dataWithString:[diction objectForKey:@"content"] classType:[diction objectForKey:@"classType"]];
    }
    return nil;
}

-(NSString *)stringToMD5String:(NSString *)string;{
    const char *cStr = [string UTF8String];
    unsigned char result[16];
    CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call
    return [NSString stringWithFormat:
            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
            ];
}

- (id)dataWithString:(NSString *)string classType:(NSNumber *)classType{

    NSInteger  type = [classType integerValue];
    switch (type) {
        case ClassTypeDictionary:{
            NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
            return  [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
        }break;
        case ClassTypeArray:
            return [string componentsSeparatedByString:@","];
            break;
        case ClassTypeData:
            return [string dataUsingEncoding:NSUTF8StringEncoding];
            break;
        case ClassTypeString:
            return string;
            break;
        default: // ClassTypeOther
            return nil;
            break;
    }
}

- (NSString *)stringWithData:(id )data{
    if ([data isKindOfClass:[NSDictionary class]]) { // 数组/字典
        NSData      *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
        return  [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }else if ([data isKindOfClass:[NSArray class]]){
        NSArray *array = data;
        return [array componentsJoinedByString:@","];
    }else if([data isKindOfClass:[NSData class]]){
        return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    }else if([data isKindOfClass:[NSString class]]){
        return data;
    }else{
        NSAssert(NO, @"数据类型不支持,目前只支持: NSDictionary;NSArray;NSString;NSData");
    }
    return nil;
}

-(NSNumber *)classTypeWithData:(id)data{
    if ([data isKindOfClass:[NSArray class]]) {
        return [NSNumber numberWithInteger:ClassTypeArray];
    }else if ([data isKindOfClass:[NSDictionary class]]){
        return [NSNumber numberWithInteger:ClassTypeDictionary];
    }else if ([data isKindOfClass:[NSString class]]){
        return [NSNumber numberWithInteger:ClassTypeString];
    }else if ([data isKindOfClass:[NSData class]]){
        return [NSNumber numberWithInteger:ClassTypeData];
    }else{
        return [NSNumber numberWithInteger:ClassTypeOther];
    }
}
@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容