[iOS學(xué)習(xí)筆記]·FMDB:第三方數(shù)據(jù)庫處理框架·用法示例篇(附源碼Demo)

關(guān)于學(xué)習(xí)使用FMDB弧关,除了筆者整理的官方文檔翻譯版,更重要的是實踐和代碼練習(xí)唤锉。筆者曾查閱過FMDB示例用法的一些文獻(xiàn)世囊,一部分有的只講到FMDatabase類,另一部分只講專為多線程操作的FMDatabaseQueue類腌紧,很少有同時介紹兩者用法區(qū)別的文章茸习。這里,筆者做一個嘗試壁肋。

  • 這里提供我整理的源碼GigHub下載鏈接号胚, 覺得有需要的朋友可以直接下載Demo,而不用看本篇文章介紹浸遗。歡迎作出進(jìn)一步優(yōu)化與分享猫胁,筆者這個嘗試也是得益于兩個大神的文章。

  • 關(guān)于FMDatabase跛锌,官方文檔說:它表示一個單獨(dú)的SQLite數(shù)據(jù)庫弃秆,用來執(zhí)行SQLite的命令。而關(guān)于FMDatabaseQueue髓帽,官方文檔說:如果你想在多線程中執(zhí)行多個查詢或更新菠赚,你應(yīng)該使用該類。這是線程安全的郑藏。

  • 然而衡查,你看完還是不知道FMDatabaseQueue是什么。在字面Queue意義上必盖,它只是個隊列拌牲?那還需要自己另外建一個DataBase嗎俱饿?其實FMDatabaseQueue這個類的對象已經(jīng)把一個數(shù)據(jù)庫和有關(guān)事務(wù)操作的方法封裝在一起了。一個FMDatabaseQueue實例對象里面包含一個FMDatabase數(shù)據(jù)庫對象塌忽。

  • Demo演示效果圖:

1. 單線程生物:FMDatabase類


1.1 對FMDB的FMDataBase類進(jìn)行一層封裝

1.1.1 源碼
  • DataBase.h
//
//  DataBase.h
//  FMDBDemo

#import <Foundation/Foundation.h>

@class Person;
@class Car;

@interface DataBase : NSObject

@property(nonatomic,strong) Person *person;

+ (instancetype)sharedDataBase;

#pragma mark - Person
/**
 *  添加person
 *
 */
- (void)addPerson:(Person *)person;
/**
 *  刪除person
 *
 */
- (void)deletePerson:(Person *)person;
/**
 *  更新person
 *
 */
- (void)updatePerson:(Person *)person;

/**
 *  獲取所有數(shù)據(jù)
 *
 */
- (NSMutableArray *)getAllPerson;

#pragma mark - Car

/**
 *  給person添加車輛
 *
 */
- (void)addCar:(Car *)car toPerson:(Person *)person;
/**
 *  給person刪除車輛
 *
 */
- (void)deleteCar:(Car *)car fromPerson:(Person *)person;
/**
 *  獲取person的所有車輛
 *
 */
- (NSMutableArray *)getAllCarsFromPerson:(Person *)person;
/**
 *  刪除person的所有車輛
 *
 */
- (void)deleteAllCarsFromPerson:(Person *)person;

@end
  • DataBase.m
//
//  DataBase.m
//  FMDBDemo

#import "DataBase.h"
#import <FMDB.h>

#import "Person.h"
#import "Car.h"

static DataBase *_DBCtl = nil;

@interface DataBase()<NSCopying,NSMutableCopying>{
    FMDatabase  *_db;
}

@end

@implementation DataBase

#pragma mark - 單例操作
+(instancetype)sharedDataBase{
    
    @synchronized(self) {
    
        if (_DBCtl == nil) {
            
            _DBCtl = [[DataBase alloc] init];
            
            [_DBCtl initDataBase];
            
        }
    }
    
    return _DBCtl;
}

+(instancetype)allocWithZone:(struct _NSZone *)zone{
    
    @synchronized(self) {
        if (_DBCtl == nil) {
            
            _DBCtl = [super allocWithZone:zone];
            
        }
    }
    return _DBCtl;  
}

-(id)copy{
    return self;
}

-(id)mutableCopy{
    return self; 
}

-(id)copyWithZone:(NSZone *)zone{
    return self;
}

-(id)mutableCopyWithZone:(NSZone *)zone{
    return self;
}

-(void)initDataBase{
    
    // 獲得Documents目錄路徑
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    
    // 文件路徑
    
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"DB1.sqlite"];
    
    // 實例化FMDataBase對象
    
    _db = [FMDatabase databaseWithPath:filePath];
    
    [_db open];
    
    // 初始化數(shù)據(jù)表
    NSString *personSql = @"CREATE TABLE 'person' ('id' INTEGER PRIMARY KEY AUTOINCREMENT  NOT NULL ,'person_id' VARCHAR(255),'person_name' VARCHAR(255),'person_age' VARCHAR(255),'person_number'VARCHAR(255)) ";
    NSString *carSql = @"CREATE TABLE 'car' ('id' INTEGER PRIMARY KEY AUTOINCREMENT  NOT NULL ,'own_id' VARCHAR(255),'car_id' VARCHAR(255),'car_brand' VARCHAR(255),'car_price'VARCHAR(255)) ";
    
    [_db executeUpdate:personSql];
    [_db executeUpdate:carSql];
    [_db close];
}

#pragma mark - 接口
#pragma mark - 對人操作
//添加人
- (void)addPerson:(Person *)person{
    [_db open];
    
    NSNumber *maxID = @(0);
    
    FMResultSet *res = [_db executeQuery:@"SELECT * FROM person "];
    //獲取數(shù)據(jù)庫中最大的ID
    while ([res next]) {
        if ([maxID integerValue] < [[res stringForColumn:@"person_id"] integerValue]) {
            maxID = @([[res stringForColumn:@"person_id"] integerValue] ) ;
        }
    }
    maxID = @([maxID integerValue] + 1);
    
    [_db executeUpdate:@"INSERT INTO person(person_id,person_name,person_age,person_number)VALUES(?,?,?,?)",maxID,person.name,@(person.age),@(person.number)];

    [_db close];
}
//刪除人
- (void)deletePerson:(Person *)person{
    [_db open];
    
    [_db executeUpdate:@"DELETE FROM person WHERE person_id = ?",person.ID];

    [_db close];
}
//更新人
- (void)updatePerson:(Person *)person{
    
    [_db open];
    
    [_db executeUpdate:@"UPDATE 'person' SET person_name = ?  WHERE person_id = ? ",person.name,person.ID];
    [_db executeUpdate:@"UPDATE 'person' SET person_age = ?  WHERE person_id = ? ",@(person.age),person.ID];
    [_db executeUpdate:@"UPDATE 'person' SET person_number = ?  WHERE person_id = ? ",@(person.number + 1),person.ID];

    [_db close];
}
//獲取所有人
- (NSMutableArray *)getAllPerson{
    
    NSMutableArray *dataArray = [[NSMutableArray alloc] init];
    
    if ([_db open]) {
        
        FMResultSet *res = [_db executeQuery:@"SELECT * FROM person"];
        
        while ([res next]) {
            
            Person *person = [[Person alloc] init];
            person.ID = @([[res stringForColumn:@"person_id"] integerValue]);
            person.name = [res stringForColumn:@"person_name"];
            person.age = [[res stringForColumn:@"person_age"] integerValue];
            person.number = [[res stringForColumn:@"person_number"] integerValue];
            
            [dataArray addObject:person];
        }
    };
    
    [_db close];
    
    return dataArray;
}

#pragma mark - 對人的車操作
/**
 *  給person添加車輛
 *
 */
- (void)addCar:(Car *)car toPerson:(Person *)person{
    [_db open];
    
    //根據(jù)person是否擁有car來添加car_id
    NSNumber *maxID = @(0);
    
    FMResultSet *res = [_db executeQuery:[NSString stringWithFormat:@"SELECT * FROM car where own_id = %@ ",person.ID]];
    
    while ([res next]) {
        if ([maxID integerValue] < [[res stringForColumn:@"car_id"] integerValue]) {
             maxID = @([[res stringForColumn:@"car_id"] integerValue]);
        }
    }
     maxID = @([maxID integerValue] + 1);
    
    [_db executeUpdate:@"INSERT INTO car(own_id,car_id,car_brand,car_price)VALUES(?,?,?,?)",person.ID,maxID,car.brand,@(car.price)];
    [_db close];
}

/**
 *  給person刪除車輛
 *
 */
- (void)deleteCar:(Car *)car fromPerson:(Person *)person{
    [_db open];
    [_db executeUpdate:@"DELETE FROM car WHERE own_id = ?  and car_id = ? ",person.ID,car.car_id];
    [_db close];
}

/**
 *  獲取person的所有車輛
 *
 */
- (NSMutableArray *)getAllCarsFromPerson:(Person *)person{
    
    [_db open];
    NSMutableArray  *carArray = [[NSMutableArray alloc] init];
    
    FMResultSet *res = [_db executeQuery:[NSString stringWithFormat:@"SELECT * FROM car where own_id = %@",person.ID]];
    while ([res next]) {
        Car *car = [[Car alloc] init];
        car.own_id = person.ID;
        car.car_id = @([[res stringForColumn:@"car_id"] integerValue]);
        car.brand = [res stringForColumn:@"car_brand"];
        car.price = [[res stringForColumn:@"car_price"] integerValue];
        [carArray addObject:car];  
    }
    [_db close];
    return carArray;
}

- (void)deleteAllCarsFromPerson:(Person *)person{
    [_db open];
    
    [_db executeUpdate:@"DELETE FROM car WHERE own_id = ?",person.ID];
    [_db close];
}

@end
1.1.2 知識點(diǎn)總結(jié)
1.1.3 說明

FMDatabase的實例對象_db在執(zhí)行SQL語句的時候采取類似下面的代碼拍埠。其中,有兩種風(fēng)格土居,一種在executeUpdate方法后面直接寫上字符串的字面量語法枣购。另一種在執(zhí)行方法的后面基于SQL字符串初始化一個NSString對象。

//刪除
[_db executeUpdate:@"DELETE FROM person WHERE person_id = ?",person.ID];

[_db executeUpdate:[NSString stringWithFormat:@"delete from %@ where name = '%@'",KTable_UserName, dto.name]];

上面注意?'%@'的區(qū)別装盯!但對于也僅僅對于SELECT查詢操作坷虑,可以省掉'%@'兩邊的''噢甲馋。DEMO代碼中也是這樣做的埂奈。

//查詢
FMResultSet *res = [_db executeQuery:[NSString stringWithFormat:@"SELECT * FROM car where own_id = '%@'",person.ID]];
//去掉''
FMResultSet *res = [_db executeQuery:[NSString stringWithFormat:@"SELECT * FROM car where own_id = %@",person.ID]];

1.2 模型層

1.2.1 源碼
  • Person.h
//
//  Person.h
//  FMDBDemo

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property(nonatomic,strong) NSNumber *ID;


@property(nonatomic,copy) NSString *name;

@property(nonatomic,assign) NSInteger age;

@property(nonatomic,assign) NSInteger number;
/**
 *  一個人可以擁有多輛車
 */
@property(nonatomic,strong) NSMutableArray *carArray;

@end

  • Car.h
//
//  Car.h
//  FMDBDemo


#import <Foundation/Foundation.h>

@interface Car : NSObject
/**
 *  所有者
 */
@property(nonatomic,strong ) NSNumber *own_id;

/**
 *  車的ID
 */
@property(nonatomic,strong) NSNumber *car_id;


@property(nonatomic,copy) NSString *brand;

@property(nonatomic,assign) NSInteger price;


@end

1.3 調(diào)用層

  • 類名
    • FMViewController.m
  • 示例方法: 添加一個人
#pragma mark - Action
/**
 *  添加數(shù)據(jù)到數(shù)據(jù)庫
 */
- (void)addData{
    
    NSLog(@"addData");
    
    int nameRandom = arc4random_uniform(1000);
    NSInteger ageRandom  = arc4random_uniform(100) + 1;
    
    
    NSString *name = [NSString stringWithFormat:@"person_%d號",nameRandom];
    NSInteger age = ageRandom;
    
    Person *person = [[Person alloc] init];
    person.name = name;
    person.age = age;

    [[DataBase sharedDataBase] addPerson:person];
    
    self.dataArray = [[DataBase sharedDataBase] getAllPerson];

    [self.tableView reloadData];
 
}
  • 知識點(diǎn)
    • arc4random_uniform(uint32_t)會隨機(jī)返回一個0到上界之間(不含上界)的整數(shù)。另外定躏,arc4random(void)這個全局函數(shù)會生成9位數(shù)的隨機(jī)整數(shù)账磺。具體參考 http://www.reibang.com/p/51269165c3e0
    • [[DataBase sharedDataBase] addPerson:person];中的sharedDataBase方法返回一個該類的靜態(tài)實例static DataBase *_DBCtl痊远。如上重寫了sharedDataBaseallocWithZone等方法垮抗,DataBase是一個單例類。

2. 多線程生物:FMDatabaseQueue類


2.1 封裝

2.1.1 單例類:負(fù)責(zé)創(chuàng)建FMDatabaseQueue和它的數(shù)據(jù)庫
  • DataBaseManager.m
//
//  DataBaseManager.m

#import "DataBaseManager.h"

@implementation DataBaseManager

//單例
+(instancetype)sharedManager{
    
    static DataBaseManager *manager = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        
        manager = [[DataBaseManager alloc] init];
        
    });
    return manager;
}

- (instancetype)init{
    
    if (self = [super init]) {
        
        NSString *docDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
        NSString *filePath = [docDir stringByAppendingPathComponent:@"DB2.sqlite"];
        
        NSLog(@"路徑 = %@",filePath);
        
        _databaseQueue = [FMDatabaseQueue databaseQueueWithPath:filePath];
    }
    return self;
}

@end
2.1.2 數(shù)據(jù)庫訪問類·基本類(DAO):負(fù)責(zé)創(chuàng)建表
  • DAO.m
//
//  DAO.m

#import "DAO.h"

@implementation DAO

- (FMDatabaseQueue *)databaseQueue{
    
    return [DataBaseManager sharedManager].databaseQueue;;
}

+ (void)creatTableIfNeed{
    
    //【注意】:以后做sql語句碧聪,()外面的參數(shù)用字符串拼冒版,()里面的參數(shù)放在執(zhí)行方法里寫
    NSString *sql = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, score REAL,arr BLOB,dic BLOB,book BLOB,date,img BLOB)",KTable_UserName];
    
    [[DataBaseManager sharedManager].databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        
        if ([db executeUpdate:sql]) {
            
            NSLog(@"創(chuàng)建表成功");
        }
    }];
}

@end

2.1.3 數(shù)據(jù)庫訪問類·用戶類(UserDAO):負(fù)責(zé)增刪改查
  • UserDAO.m
//
//  UserDAO.m

#import "UserDAO.h"
#import "UserDTO.h"

@implementation UserDAO

///單例
+(instancetype)sharedInstance{
    
    static UserDAO *instance = nil;
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        //1.
        instance = [[UserDAO alloc] init];
        
    });
    return instance;
}

/*!
 *  插入數(shù)據(jù)
 *  【注意】:?逞姿?辞嗡?
 */
- (BOOL)insertUserDTO:(UserDTO *)dto{
    
    __block BOOL success = NO;

    NSString *sql = [NSString stringWithFormat:@"insert into %@ (name,age,score,arr,dic,book,date,img) values (?,?,?,?,?,?,?,?)",KTable_UserName];
    
    [self.databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        
        if (![db executeUpdate:sql,
              dto.name,
              @(dto.age),
              @(dto.score),
              [NSKeyedArchiver archivedDataWithRootObject:dto.arr],
              [NSKeyedArchiver archivedDataWithRootObject:dto.dic],
              [NSKeyedArchiver archivedDataWithRootObject:dto.book],
              dto.date,
               UIImagePNGRepresentation(dto.img)]) {
            
            *rollback = YES;
            return ;
        }
        
        success = YES;
    }];
    
    return success;
}

/*!
 *  獲取數(shù)據(jù)
 */
- (NSMutableArray *)loadUserData{

    NSString *sql = [NSString stringWithFormat:@"select * from %@",KTable_UserName];
    
    NSMutableArray *arrM = [NSMutableArray array];
    
    [self.databaseQueue inDatabase:^(FMDatabase *db) {
        
       FMResultSet *re = [db executeQuery:sql];
        
        while ([re next]) {
            
            UserDTO *dto = [[UserDTO alloc] init];
            dto.name = [re stringForColumn:@"name"];
            dto.age = [re intForColumn:@"age"];
            dto.score = [re doubleForColumn:@"score"];
            dto.arr = [NSKeyedUnarchiver unarchiveObjectWithData:[re dataForColumn:@"arr"]];
            dto.dic = [NSKeyedUnarchiver unarchiveObjectWithData:[re dataForColumn:@"dic"]];
            dto.book = [NSKeyedUnarchiver unarchiveObjectWithData:[re dataForColumn:@"book"]];
            dto.date = [re dateForColumn:@"date"];
            dto.img = [UIImage imageWithData:[re dataForColumn:@"img"]];
            
            [arrM addObject:dto];
            
             NSLog(@"名字 = %@",dto.name);
             NSLog(@"數(shù)組 = %@",dto.arr);
             NSLog(@"字典 = %@",dto.dic);
             NSLog(@"BOOK = %@",dto.book);
             NSLog(@"時間 = %@",dto.date);
        }
    }];

    return arrM;
}

/*!
 *  修改數(shù)據(jù)
 * 【注意】
    1.這個參數(shù),可要又不要滞造,最好要续室,然后把新的模型賦值過去
    2.修改兩個值的格式是: set a = 'a' , b = 'b'  一定不要少了中間的逗號
    3.是單引號,不可使用雙引號谒养,所有的都是
 */
- (BOOL)updateUserDTO:(UserDTO *)dto{

    __block BOOL success = NO;
    
    NSString *sql = [NSString stringWithFormat:@"update %@ set name = '%@',score = '%@'",KTable_UserName,@"郭美美",@(100)];

    [self.databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        
        if (![db executeUpdate:sql]) {
            
            *rollback = YES;
            return ;
        }
        
        success = YES;
    }];

    return success;
}

/*!
 *  刪除某個模型  (簡單)
 */
- (BOOL)deleteUserDTO:(UserDTO *)dto{

    __block BOOL success = NO;
    
    NSString *sql = [NSString stringWithFormat:@"delete from %@ where name = '%@'",KTable_UserName, dto.name];
    
    [self.databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        
        if (![db executeUpdate:sql]) {
            
            *rollback = YES;
            return ;
        }
        
        success = YES;
    }];
    
    return success;
}


/*!
 *  刪除某個模型  (簡單)
 */
- (BOOL)deleteAllUserDTO{
    
    __block BOOL success = NO;
    
    NSString *sql = [NSString stringWithFormat:@"delete from %@",KTable_UserName];
    
    [self.databaseQueue inTransaction:^(FMDatabase *db, BOOL *rollback) {
        
        if (![db executeUpdate:sql]) {
            
            *rollback = YES;
            return ;
        }
        
        success = YES;
    }];
    
    return success;
}

@end

2.2 模型層

2.2.1 用戶數(shù)據(jù)操作對象UserDTO
  • UserDTO.h
//
//  UserDTO.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class BookDTO;

@interface UserDTO : NSObject

/** 基本屬性 */
@property (nonatomic, copy) NSString *name;

@property (nonatomic, assign) NSInteger age;

@property (nonatomic, assign) float score;

/** 數(shù)組 */
@property (nonatomic, strong) NSArray *arr;

/** 字典 */
@property (nonatomic, strong) NSDictionary *dic;

/** 自定義模型 */
@property (nonatomic, strong) BookDTO *book;

/** 時間 */
@property (nonatomic, strong) NSDate *date;

/** 圖片 */
@property (nonatomic, strong) UIImage *img;


///構(gòu)造器1
+ (UserDTO *)userWithUserName:(NSString *)userName
                      userAge:(NSInteger)userAge
                    userScore:(float)userScore
                          arr:(NSArray *)arr
                          dic:(NSDictionary *)dic
                         book:(BookDTO *)book
                         date:(NSDate *)date
                        image:(UIImage *)img;

@end
2.2.2 書本數(shù)據(jù)操作對象BookDTO
  • BookDTO.h
//
//  BookDTO.h

#import <Foundation/Foundation.h>

@interface BookDTO : NSObject

@property (nonatomic, copy) NSString *bookName;

@property (nonatomic, assign) NSInteger bookId;

/** 創(chuàng)建book */
+ (BookDTO *)bookWithBookName:(NSString *)bookName bookId:(NSInteger)bookId;


@end

2.3 調(diào)用層

  • 類名
    • DBQViewController.m
  • 示例方法:
#pragma mark - Action
/**
 *  添加數(shù)據(jù)到數(shù)據(jù)庫
 */
- (void)addData{
    
    NSLog(@"addData");
    
    int nameRandom = arc4random_uniform(1000);
    NSInteger ageRandom  = arc4random_uniform(100) + 1;

    NSString *name = [NSString stringWithFormat:@"person_%d號",nameRandom];
    NSInteger age = ageRandom;

    NSArray *arrr = @[@"t1",@"t2",@"t3"];
    
    NSDictionary *dic = @{@"key":@"t123"};
    
    BookDTO *bDto = [BookDTO bookWithBookName:@"NewBook" bookId:0011];
    
    UIImage *imag = [UIImage imageNamed:@"1"];
    
    NSLog(@"date = %@",[NSDate date]);
    //2016-05-11 10:04:50 +0000
    //2016-05-11 10:05:40 +0000  13
    //2016-05-11 10:06:01 +0000    31
    
    UserDTO *dto = [UserDTO userWithUserName:name userAge:age userScore:9 arr:arrr dic:dic book:bDto date:[NSDate date] image:imag];
    
    if ([[UserDAO sharedInstance] insertUserDTO:dto]) {
        
        NSLog(@"插入成功");
    }else{
        
        NSLog(@"插入失敗");
    }
    
    self.dataArray = [[UserDAO sharedInstance] loadUserData];
    [self.tableView reloadData];
}

3. 小結(jié)

  • FMDatabase創(chuàng)建數(shù)據(jù)庫的路徑代碼為:
// 獲得Documents目錄路徑
    NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    
    // 文件路徑
    
    NSString *filePath = [documentsPath stringByAppendingPathComponent:@"DB1.sqlite"];
  • FMDatabaseQueue創(chuàng)建數(shù)據(jù)庫的路徑代碼為:
if (self = [super init]) {
        
        NSString *docDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
        NSString *filePath = [docDir stringByAppendingPathComponent:@"DB2.sqlite"];
        
        NSLog(@"路徑 = %@",filePath);
        
        _databaseQueue = [FMDatabaseQueue databaseQueueWithPath:filePath];
    }
    return self;
  • 運(yùn)行的時候挺狰,你可以打個斷點(diǎn),打印出數(shù)據(jù)庫的路徑买窟,類似如下:
/Users/ChenMan/Library/Developer/CoreSimulator/Devices/12B1701D-AFF5-4D6B-9923-86CE8AE0C387/data/Containers/Data/Application/6512C7E8-BD84-47FC-9284-00E7BBBD67D8/Documents/
  • 回到電腦桌面丰泊,按住快捷鍵shift+command+G,到達(dá)如上文件目錄始绍,可以看到文件目錄結(jié)構(gòu)類似如下:
  • 用Navicat軟件打開.sqlite文件可以可視化的查看表的數(shù)據(jù)瞳购,類似如下:

參考文獻(xiàn):

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市疆虚,隨后出現(xiàn)的幾起案子苛败,更是在濱河造成了極大的恐慌满葛,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罢屈,死亡現(xiàn)場離奇詭異嘀韧,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)缠捌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門锄贷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人曼月,你說我怎么就攤上這事谊却。” “怎么了哑芹?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵炎辨,是天一觀的道長。 經(jīng)常有香客問我聪姿,道長碴萧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任末购,我火速辦了婚禮破喻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘盟榴。我一直安慰自己曹质,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布擎场。 她就那樣靜靜地躺著羽德,像睡著了一般。 火紅的嫁衣襯著肌膚如雪顶籽。 梳的紋絲不亂的頭發(fā)上玩般,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機(jī)與錄音礼饱,去河邊找鬼坏为。 笑死,一個胖子當(dāng)著我的面吹牛镊绪,可吹牛的內(nèi)容都是我干的匀伏。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蝴韭,長吁一口氣:“原來是場噩夢啊……” “哼够颠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起榄鉴,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤履磨,失蹤者是張志新(化名)和其女友劉穎蛉抓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體剃诅,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巷送,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了矛辕。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笑跛。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖聊品,靈堂內(nèi)的尸體忽然破棺而出飞蹂,到底是詐尸還是另有隱情,我是刑警寧澤翻屈,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布陈哑,位于F島的核電站,受9級特大地震影響妖胀,放射性物質(zhì)發(fā)生泄漏芥颈。R本人自食惡果不足惜惠勒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一赚抡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纠屋,春花似錦涂臣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至族铆,卻和暖如春岩四,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哥攘。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工剖煌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逝淹。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓耕姊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親栅葡。 傳聞我的和親對象是個殘疾皇子茉兰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

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