從0開始弄一個面向OC數(shù)據(jù)庫(一)

近期又學習了一下數(shù)據(jù)庫的東西裆站,決定花時間封裝一個數(shù)據(jù)庫宏胯,看了一些源碼肩袍,感覺有一定的可行度婚惫,所以先把第一篇(本篇)文章發(fā)上來先舷。啥都沒有發(fā)上來干啥捏密浑?有時候容易半途而廢尔破,先發(fā)上來浇衬,斷自己后路耘擂!一定得寫,不然就是說話不算話了篙悯。
100婚
1鸽照、我們要做什么矮燎?

這個文章赔癌,我們要從0開始封裝一個面向OC對象的數(shù)據(jù)庫灾票,想了解怎么做的刊苍,可以一起陪伴一下班缰,所有的流程細節(jié)我都會寫在文章內(nèi)埠忘,因為我也第一次搞這個東西,有興趣的話咱們可以一起討論和提升名船。

2渠驼、做成什么樣子迷扇?

當然是期望做到簡()單()操作數(shù)據(jù)庫蜓席,不需要背語句课锌,也不需要解析模型,類似realm這種騷操作[realm addObject:obj];obj就給你存到數(shù)據(jù)庫了请毛。


當然获印,我們不能和行業(yè)航母去比較兼丰,但是封裝出來的東西一定要簡單適用鳍征,安全可靠面徽。我們封裝的數(shù)據(jù)庫趟紊,操作必須簡單霎匈,功能必須全面(增刪查改一樣不能少铛嘱,數(shù)據(jù)庫遷移墨吓,數(shù)據(jù)庫字段改名也得支持帖烘,多線程安全也得考慮,最重要的是常用的數(shù)據(jù)類型我們都得支持)照卦。

3窄瘟、簡單介紹

(本來說好的只發(fā)文章斷自己后路,但是還是先實現(xiàn)一部分功能锄列,免得顯得太空洞了)
本篇主要實現(xiàn)的功能有:

  • 1邻邮、整個庫的結(jié)構(gòu)設(shè)計筒严;
  • 2鸭蛙、打開并創(chuàng)建數(shù)據(jù)庫、關(guān)閉數(shù)據(jù)庫晒哄;
  • 3寝凌、根據(jù)模型對象较木,創(chuàng)建對應(yīng)的數(shù)據(jù)庫表格劫映;
    在做功能之前泳赋,先簡單的介紹一下數(shù)據(jù)庫相關(guān)的東西:
    數(shù)據(jù)庫.png

    以上的圖表示祖今,名稱為CWDB的數(shù)據(jù)庫千诬,里面有一張Student53class的表徐绑,表有的字段為age傲茄,stuId盘榨,score草巡,height山憨,name。也可以解釋為CWDB這個數(shù)據(jù)庫里面存了53班每個學生的年齡侣颂,學號憔晒,成績拒担,身高从撼,名字,可惜的是53班目前沒有一個學生=钧栖。=
    一個數(shù)據(jù)庫里面可以有多張名字不重復(fù)的表低零,一個表里面可以有多條主鍵不一致的數(shù)據(jù),每條數(shù)據(jù)指定一個字段為主鍵當唯一標識拯杠。
    查看數(shù)據(jù)庫這里我用了一個破解版的工具,現(xiàn)在提供給大家:
    鏈接: https://pan.baidu.com/s/1eSIpTBW 密碼: 4rjm
3掏婶、開始動手

首先創(chuàng)建一個工程,并向工程中拖入libsqlite3.0.tbd這個庫

1潭陪、庫的結(jié)構(gòu)設(shè)計
結(jié)構(gòu).png
2、打開并創(chuàng)建數(shù)據(jù)庫依溯、關(guān)閉數(shù)據(jù)庫

a老厌、打開并創(chuàng)建數(shù)據(jù)庫
sqlite3 向我們提供了這個接口,用來執(zhí)行打開數(shù)據(jù)庫操作黎炉,第一個參數(shù)為數(shù)據(jù)庫存的路徑枝秤,第二個參數(shù)為sqlite3的操作連接。
如果數(shù)據(jù)庫路徑下沒有數(shù)據(jù)庫慷嗜,則創(chuàng)建一個數(shù)據(jù)庫并打開淀弹,如果有則直接打開數(shù)據(jù)庫

SQLITE_API int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);

我們封裝一個方法執(zhí)行創(chuàng)建并打開數(shù)據(jù)庫的代碼丹壕,當傳uid的時候會以uid命名數(shù)據(jù)庫,如果沒傳將會默認數(shù)據(jù)庫名稱為CWDB垦页,路徑我們寫在CWDatabase.m下雀费,因為是測試階段干奢,所以路徑設(shè)置在桌面上痊焊。需要自行修改路徑

+ (BOOL)openDB:(NSString *)uid {
    // 數(shù)據(jù)庫名稱
    NSString *dbName = @"CWDB.sqlite";
    if (uid.length != 0) {
        dbName = [NSString stringWithFormat:@"%@.sqlite", uid];
    }
    // 數(shù)據(jù)庫路徑
    NSString *dbPath = [kCWDBCachePath stringByAppendingPathComponent:dbName];
    // 打開數(shù)據(jù)庫
    int result = sqlite3_open(dbPath.UTF8String, &cw_database);
    if (result != SQLITE_OK) {
        NSLog(@"打開數(shù)據(jù)庫失敗! : %d",result);
        return NO;
    }
    // 檢測當前連接的數(shù)據(jù)庫是否處于busy狀態(tài)兵怯,處于則會回調(diào)CWDBBusyCallBack
    sqlite3_busy_handler(cw_database, &CWDBBusyCallBack, (void *)(cw_database));
    
    return YES;
}

b幸缕、關(guān)閉數(shù)據(jù)庫
傳一個sqlite3的操作連接即可以將連接關(guān)閉

SQLITE_API int sqlite3_close(sqlite3*);

帖上我們對應(yīng)的代碼

+ (void)closeDB {
    if (cw_database) {
        sqlite3_close(cw_database);
        cw_database = nil;
    }
}

c、進行單元測試

選擇如下圖創(chuàng)建一個單元測試的類
unitTest.png

然后寫上我們的測試代碼
test.png

點擊箭頭所指向的框框似扔,則只能測試本函數(shù)逛尚,通過第43行的斷言來判斷result是否為YES垄惧,是YES則測試通過,然后框框內(nèi)會變成一個綠色的勾绰寞,測試不通過則會變成紅色的叉到逊,然后我們看看我們對應(yīng)的位置有沒有成功創(chuàng)建一個數(shù)據(jù)庫,最終我們發(fā)現(xiàn)測試通過滤钱。

3觉壶、根據(jù)模型對象,創(chuàng)建對應(yīng)的數(shù)據(jù)庫表格件缸;

a铜靶、調(diào)用sqlite3的API創(chuàng)建表格
sqlite為我們提供下面這個方法在執(zhí)行sql語句

//數(shù)據(jù)庫執(zhí)行語句
SQLITE_API int sqlite3_exec(
  sqlite3*,                                  /* sqlite3的操作連接 */
  const char *sql,                           /* SQL語句 */
  int (*callback)(void*,int,char**,char**),  /* 回調(diào)函數(shù) */
  void *,                                    /* 第一個參數(shù)的回調(diào) */
  char **errmsg                              /* 錯誤信息 */
);

做數(shù)據(jù)庫執(zhí)行語句時,我們的邏輯是:

  • 1.打開數(shù)據(jù)庫
  • 2.數(shù)據(jù)庫執(zhí)行語句
  • 3.關(guān)閉數(shù)據(jù)庫
    我們在CWDatabase封裝一個方法他炊,用來執(zhí)行數(shù)據(jù)庫操作,第一個參數(shù)為需要執(zhí)行的sql語句争剿,第二個參數(shù)為userId,用來打開對應(yīng)的數(shù)據(jù)庫
+ (BOOL)execSQL:(NSString *)sql uid:(NSString *)uid {
    // 打開數(shù)據(jù)庫
    if (![self openDB:uid]) {
        return NO;
    }
    // 執(zhí)行語句
    char *errmsg = nil;
    int result = sqlite3_exec(cw_database, sql.UTF8String, nil, nil, &errmsg);
    // 關(guān)閉數(shù)據(jù)庫
    [self closeDB];
    // 執(zhí)行語句失敗則拋出錯誤信息
    if (result != SQLITE_OK) {
        NSLog(@"exec sql error : %s",errmsg);
        return NO;
    }
    return YES;
}

同樣的痊末,我們對這個方法進行單元測試蚕苇,在這里我們需要自己寫sql的執(zhí)行語句,測試傳uid與不傳uid兩種情況,并斷言會成功

- (void)testOpenDBAndExceSql {
    NSString *sql = @"create table if not exists Student(id integer , name text not null, age integer, score real,primary key(id))";
    
    BOOL result = [CWDatabase execSQL:sql uid:nil];
    XCTAssertEqual(YES, result);
    
    BOOL result1 = [CWDatabase execSQL:sql uid:@"Chavez"];
    XCTAssertEqual(YES, result1);
}

最終成功創(chuàng)建對應(yīng)的兩個數(shù)據(jù)庫以及表格

表格.png

b凿叠、面向模型來創(chuàng)建數(shù)據(jù)庫表格
做完以上步驟捆蜀,我們成功的通過調(diào)用sqlite3的API準確的創(chuàng)建了一個表格,但是還是要背sql語句幔嫂,和我們一開始的初衷相違背辆它,我們需要的是簡單、簡單履恩、簡單锰茉、無腦操作。切心。所以我們需要想個辦法來省去背sql語句這一步驟飒筑。
簡單分析一下創(chuàng)建表格的sql語句片吊,我們會發(fā)現(xiàn)可以分成下面的結(jié)構(gòu)

create table if not exists Student(id integer , name text not null, age integer, score real,primary key(id))
create table if not exists 表名(字段1 字段1類型,字段2 字段2類型 ....., primary key(字段))

其中字段和字段類型,可以對應(yīng)成操作模型的成員變量以及成員變量的類型协屡,所以俏脊,我們通過runtime的方法,獲取到模型的所有成員變量以及所有成員變量對應(yīng)的類型肤晓。
我們在CWModelTool這個類里面封裝一個方法來獲取模型所有成員變量的類型以及名稱爷贫,封裝成一個字典返回 字典的類型為 {成員變量名稱(key) :成員變量類型(value)}

+ (NSDictionary *)classIvarNameAndTypeDic:(Class)cls {
    unsigned int outCount = 0;
    Ivar *varList = class_copyIvarList(cls, &outCount);
    NSMutableDictionary *nameTypeDic = [NSMutableDictionary dictionary];
    
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = varList[i];
        // 1.獲取成員變量名稱
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        if ([ivarName hasPrefix:@"_"]) {
            ivarName = [ivarName substringFromIndex:1];
        }
        
        // 2.獲取成員變量類型 @\"
        NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        
        type = [type stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"@\""]];
        
        [nameTypeDic setValue:type forKey:ivarName];
        
    }
    
    return nameTypeDic;
}

然后我們進行單元測試,創(chuàng)建CWModelToolTests的單元測試并創(chuàng)建一個student的模型补憾,模型的成員變量為

@interface Student : NSObject<CWModelProtocol>
{
    float score;
}
@property (nonatomic,assign) int stuId; // 學號
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@property (nonatomic,assign) int height;
@end

然后我們在CWModelToolTests寫一個單元測試的方法

- (void)testIvarNameTypeDict {
    NSDictionary *dict = [CWModelTool classIvarNameAndTypeDic:[Student class]];
    NSLog(@"Student------%@",dict);
    XCTAssertNotNil(dict);
}

然后我們運行這個測試函數(shù)漫萄,在控制臺得到如下打印:

2017-12-07 16:41:23.934525+0800 CWDB[34996:3867985] Student------{
    age = i;
    height = i;
    name = NSString;
    score = f;
    stuId = i;
}

與對應(yīng)模型的成員變量一致盈匾,測試通過腾务。
獲取了對應(yīng)的成員變量的字典后,我們需要將這個字典轉(zhuǎn)換成sql對應(yīng)的語句削饵,下面加粗的部分
create table if not exists 表名(字段1 字段1類型,字段2 字段2類型 ....., primary key(字段))
在此之前岩瘦,我們還要進行另一個轉(zhuǎn)換,因為數(shù)據(jù)庫里面對應(yīng)的類型和OC的類型并不一樣窿撬,所以要變一變

暫時不考慮OC對象(數(shù)組启昧,字典 等...)以及自定義對象的情況

 OC                                      數(shù)據(jù)庫
 i :         整型                        integer
 q:          long                       integer
 Q:          long long                  integer   
 B:          bool                       integer
 d:          double                     real
 f:          float                      real
 NSString:   字符串                      text      
 NSData:     二進制                      blob   

我們封裝一個函數(shù)來進行字典的轉(zhuǎn)換,我們要得到的字典類型{成員變量名稱(key) :成員變量對應(yīng)數(shù)據(jù)庫的類型(value)}

+ (NSDictionary *)classIvarNameAndSqlTypeDic:(Class)cls {
    // 獲取模型的所有成員變量
    NSMutableDictionary *classDict = [[self classIvarNameAndTypeDic:cls] mutableCopy];
    
    [classDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL * _Nonnull stop) {
        // 對應(yīng)的數(shù)據(jù)庫的類型重新賦值
        classDict[key] = [self getSqlType:obj];
    }];
    return classDict;
}

// oc類型轉(zhuǎn)換到數(shù)據(jù)庫的類型
+ (NSString*)getSqlType:(NSString*)type{
    if([type isEqualToString:@"i"]||[type isEqualToString:@"I"]||
       [type isEqualToString:@"s"]||[type isEqualToString:@"S"]||
       [type isEqualToString:@"q"]||[type isEqualToString:@"Q"]||
       [type isEqualToString:@"b"]||[type isEqualToString:@"B"]||
       [type isEqualToString:@"c"]||[type isEqualToString:@"C"]|
       [type isEqualToString:@"l"]||[type isEqualToString:@"L"]) {
        return @"integer";
    }else if([type isEqualToString:@"f"]||[type isEqualToString:@"F"]||
             [type isEqualToString:@"d"]||[type isEqualToString:@"D"]){
        return @"real";
    }else if ([type isEqualToString:@"NSData"]) {
        return @"blob";
    }else{
        return @"text";
    }
}

這里我們就不在貼測試的代碼了尤仍,反正是成功的箫津。
然后我們將以上方法獲取的字典轉(zhuǎn)換成我們需要的sql的字符串,也就是這種類型 字段1 字段1類型,字段2 字段2類型 .....聲明主鍵后面在拼接

+ (NSString *)sqlColumnNamesAndTypesStr:(Class)cls {
    NSDictionary *sqlDict = [[self classIvarNameAndSqlTypeDic:cls] mutableCopy];
    NSMutableArray *nameTypeArr = [NSMutableArray array];

    [sqlDict enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *obj, BOOL * _Nonnull stop) {
        [nameTypeArr addObject:[NSString stringWithFormat:@"%@ %@",key,obj]];
    }];
    
    return [nameTypeArr componentsJoinedByString:@","];
}

同理。宰啦。這里我們也不測試了苏遥。反正是成功的
create table if not exists 表名(字段1 字段1類型,字段2 字段2類型 ....., primary key(字段))
這段語句,我們還差一個表名和主鍵沒有獲取下面我們給CWModelTool封裝一個方法來獲取表名赡模,表名我們是通過模型的類名拼接targetid組成的田炭。

+ (NSString *)tableName:(Class)cls targetId:(NSString *)targetId {
    return [NSString stringWithFormat:@"%@%@",NSStringFromClass(cls),targetId];
}

在獲取主鍵這里,有兩種常用的方式漓柑,一種是設(shè)計一個自動增長的主鍵教硫,另一種是學習realm的方式,通過代理讓用戶為模型返回一個主鍵辆布,這里我們使用后者瞬矩。在CWModelProtocol聲明一個協(xié)議方法,且這個方法是必須實現(xiàn)的

@protocol CWModelProtocol <NSObject>

@required
/**
 操作模型必須實現(xiàn)的方法,通過這個方法獲取主鍵信息
 
 @return 主鍵字符串
 */
+ (NSString *)primaryKey;

@end

接下來锋玲,我們封裝創(chuàng)建數(shù)據(jù)庫表格的最終方法
在CWSqliteModelTool內(nèi)景用,封裝一個方法

// uid用來確認哪個數(shù)據(jù)庫,targetId用來區(qū)分數(shù)據(jù)庫表名
+ (BOOL)createSQLTable:(Class)cls uid:(NSString *)uid targetId:(NSString *)targetId {
    // 創(chuàng)建數(shù)據(jù)庫表的語句
    // create table if not exists 表名(字段1 字段1類型(約束),字段2 字段2類型(約束)....., primary key(字段))
    // 獲取數(shù)據(jù)庫表名
    NSString *tableName = [CWModelTool tableName:cls targetId:targetId];
    
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要操作這個模型惭蹂,必須要實現(xiàn)+ (NSString *)primaryKey;這個方法伞插,來告訴我主鍵信息");
        return NO;
    }
    // 獲取主鍵
    NSString *primaryKey = [cls primaryKey];
    if (!primaryKey) {
        NSLog(@"你需要指定一個主鍵來創(chuàng)建數(shù)據(jù)庫表");
        return NO;
    }
    
    NSString *createTableSql = [NSString stringWithFormat:@"create table if not exists %@(%@, primary key(%@))",tableName,[CWModelTool sqlColumnNamesAndTypesStr:cls],primaryKey];
    
    return [CWDatabase execSQL:createTableSql uid:uid];
}

然后割粮。。我們來進行單元測試
新建一個CWSqliteModelToolTests單元測試類媚污,用來測試CWSqliteModelTool的所有方法,然后新建一個Student模型舀瓢,遵守CWModelProtocol協(xié)議,實現(xiàn)必須要的協(xié)議方法耗美。

Student.h
#import <Foundation/Foundation.h>
#import "CWModelProtocol.h"

@interface Student : NSObject<CWModelProtocol>
{
    float score;
}
@property (nonatomic,assign) int stuId; // 學號
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) int age;
@property (nonatomic,assign) int height;
@end


Student.m
#import "Student.h"

@implementation Student
// 返回主鍵信息
+ (NSString *)primaryKey {
    return @"stuId";
}

@end

測試創(chuàng)建數(shù)據(jù)庫表格方法

- (void)testCreateSQLTable {
    BOOL result = [CWSqliteModelTool createSQLTable:[Student class] uid:@"CWDB" targetId:@"53class"];
    XCTAssertTrue(result);
}

運行之后得到如下結(jié)果


image.png

在對應(yīng)的路徑下京髓,創(chuàng)建了CWDB數(shù)據(jù)庫,并在數(shù)據(jù)庫里面創(chuàng)建一張Student53class的表幽歼,表的列名與Student模型的成員變量一一對應(yīng)朵锣,測試通過谬盐!

用戶如果要創(chuàng)建一個表甸私,只需要調(diào)用這個方法

+ (BOOL)createSQLTable:(Class)cls uid:(NSString *)uid targetId:(NSString *)targetId;

將模型的類型,用戶id(可以為空)以及目標id(可以為空)傳過來飞傀,我們就會創(chuàng)建對應(yīng)的數(shù)據(jù)庫并打開皇型,解析模型,創(chuàng)建對應(yīng)的表格砸烦,關(guān)閉數(shù)據(jù)庫弃鸦。

4、本篇結(jié)束

在此幢痘,我們通過調(diào)用sqlite的API唬格,通過runtime,將創(chuàng)建數(shù)據(jù)庫表格的操作用非常簡潔的API開放出來颜说,目前還是很成功的购岗,在下一篇文章,我們會實現(xiàn)數(shù)據(jù)庫插入门粪、查詢喊积、更新操作。玄妈。在更后面的文章乾吻,我們會實現(xiàn)刪除、存儲模型內(nèi)嵌套OC對象拟蜻,以及數(shù)組內(nèi)嵌套自定義模型绎签,以及多線程安全等的處理。酝锅。

每一章的代碼我會上傳到github上诡必。。并打tag作為一個節(jié)點屈张。歡迎大家下載并查找漏洞擒权,因為袱巨。我也是第一次封裝。一起學習碳抄,一起進步愉老。

github地址
tag為1.0.0,你可以在下圖的位置找到他剖效,并下載下來嫉入。

image.png

最后覺得有用的同學,希望能給本文點個喜歡璧尸,給github點個star以資鼓勵咒林,謝謝大家。

PS: 因為我也是一邊封裝爷光,一邊寫文章垫竞。效率可能比較低,問題也會有蛀序,歡迎大家向我拋issue欢瞪,有更好的思路也歡迎大家留言!

目前第二篇文章已經(jīng)出爐,地址:從0開始弄一個面向OC數(shù)據(jù)庫(二)

最后再為大家推薦一個0耦合的側(cè)滑框架徐裸。
一行代碼集成超低耦合的側(cè)滑功能

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末遣鼓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子重贺,更是在濱河造成了極大的恐慌骑祟,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件气笙,死亡現(xiàn)場離奇詭異次企,居然都是意外死亡,警方通過查閱死者的電腦和手機健民,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門抒巢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人秉犹,你說我怎么就攤上這事蛉谜。” “怎么了崇堵?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵型诚,是天一觀的道長。 經(jīng)常有香客問我鸳劳,道長狰贯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮涵紊,結(jié)果婚禮上傍妒,老公的妹妹穿的比我還像新娘。我一直安慰自己摸柄,他們只是感情好颤练,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著驱负,像睡著了一般嗦玖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上跃脊,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天宇挫,我揣著相機與錄音,去河邊找鬼酪术。 笑死器瘪,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拼缝。 我是一名探鬼主播娱局,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼彰亥,長吁一口氣:“原來是場噩夢啊……” “哼咧七!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起任斋,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤继阻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后废酷,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘟檩,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年澈蟆,在試婚紗的時候發(fā)現(xiàn)自己被綠了墨辛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡趴俘,死狀恐怖睹簇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寥闪,我是刑警寧澤太惠,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站疲憋,受9級特大地震影響凿渊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一埃脏、第九天 我趴在偏房一處隱蔽的房頂上張望搪锣。 院中可真熱鬧,春花似錦彩掐、人聲如沸淤翔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旁壮。三九已至,卻和暖如春谐檀,著一層夾襖步出監(jiān)牢的瞬間抡谐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工桐猬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留麦撵,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓溃肪,卻偏偏與公主長得像免胃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惫撰,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

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