iOS 本地存储数据的几种方式

写在前面:iOS本地持久化存储的路径
Documents: 最常用的目录,存放重要的数据,iTunes同步时会备份该目录
Library/Caches: 一般存放体积大,不重要的数据,iTunes同步时不会备份该目录
Library/Preferences: 存放用户的偏好设置,iTunes同步时会备份该目录
tmp: 用于存放临时文件,在程序未运行时可能会删除该文件夹中的数据,iTunes同步时不会备份该目录
存储方式:NSUserDefaultsPlistNSKeyedArchiverSQLite3Core DataKeychainFMDB


一、NSUserDefaults 方式存储

1.1 写入

 NSUserDefaults *login = [NSUserDefaults standardUserDefaults];
 [login setObject:self.passwordField.text forKey:@"token"];
 [login synchronize];

1.2 读取

 NSUserDefaults *login = [NSUserDefaults standardUserDefaults];
 NSString *str = [login objectForKey:@"token"];

\color{#f5576c}{写在后面:}
1.只能存储OC常用数据类型(NSString、NSDictionary、NSArray、NSData、NSNumber等类型)而不能直接存储自定义数据。
2.键值对存储,直接指定存储类型。


二、plist 方式存储

2.1 写入

#pragma mark - 保存到本地
- (void)saveToLocale {
    [self.view endEditing:YES];
    if (!self.nameField.text.length) {
        [TipUtils showToast:self.view message:@"应用名称不能为空"];
        return;
    }
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:@"password.plist"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL Exists = [fileManager fileExistsAtPath:path];
    if (!Exists) {
        _serectArray = [[NSMutableArray alloc] init];
    } else {
        _serectArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
    }
    // 这里是修改更新数据
    for (int i=0;i <self.serectArray.count;i++) {
        if ([self.myPassword.timeID isEqualToString:self.serectArray[i][@"timeID"]]) {
            self.serectArray[i][@"name"] = self.nameField.text;
            [self.serectArray writeToFile:path atomically:YES];
            if ([self.serectArray writeToFile:path atomically:YES]) {
                [TipUtils showToast:self.view message:@"保存成功"];
                [self  performSelector:@selector(delayMethod) withObject:nil afterDelay:1.0f];
            } else {
                NSLog(@"保存失败");
            }
            return;
        }
    }
    NSDictionary *dict = [NSDictionary dictionary];
    dict = @{@"timeID":[self getItemID], @"name":self.nameField.text};
    [self.serectArray addObject:dict];
    [self.serectArray writeToFile:path atomically:YES];
    if ([self.serectArray writeToFile:path atomically:YES]) {
        [TipUtils showToast:self.view message:@"保存成功"];
        [self  performSelector:@selector(delayMethod) withObject:nil afterDelay:1.0f];
        NSLog(@"存储的数据有:%@",self.serectArray);
    } else {
        NSLog(@"保存失败");
    }
}

#pragma mark - 延时函数
- (void)delayMethod {
    [self.navigationController popViewControllerAnimated:YES];
}

2.2 读取

#pragma mark - 读取数据
- (void)readDataFromPlist {
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:@"password.plist"];
NSLog(@"存储路径-%@",path);
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL Exists = [fileManager fileExistsAtPath:path];
    if (!Exists) {
        _serectArray = [[NSMutableArray alloc] init];
    } else {
        _serectArray = [[NSMutableArray alloc] initWithContentsOfFile:path];
    }
}

\color{#f5576c}{写在后面:}
1.只能存储OC常用数据类型(NSString、NSDictionary、NSArray、NSData、NSNumber等类型)而不能直接存储自定义模型对象。
· 如果想用对象,只能再用明杰的框架进行字典转模型了。
· 或者想存储自定义模型对象 -> 只能将自定义模型对象转换为字典存储;
2.系统的plist文件,只能读取,不行写入,自定义的可读可写
3.plist文件存储的位置,但一般存在Documents中
4.如果存储图片路径的话,一定要存储相对位置,因为每次启动APP,plist文件的路径就会变化,自然图片的位置也就变化了。


三、NSKeyedArchiver归档(NSCoding)
  • 归档解档最大的好处在于可以存储自定义对象数据
  • 归档解档要注意ios版本
  1. 新建一个数据模型类Person
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCoding>
@property(nonatomic, copy) NSString *name;
@end
#import "Person.h"
@implementation Person
-(id)init {
    if (self == nil) {
        self = [super init];
    }
    return self;
}
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
    [aCoder encodeObject:self.name forKey:@"name"];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
    if (self = [super init]) {
        self.name = [aDecoder decodeObjectForKey:@"name"];
    }
    return self;
}
@end
  1. 新建一个归档解档通用工具类ArchiveTools
#import <Foundation/Foundation.h>
@interface ArchiveTools : NSObject
+ (BOOL)archiveObject:(id)object prefix:(NSString *)prefix;
+ (id)unarchiveClass:(Class)class prefix:(NSString *)prefix;
+ (NSString *)getPathWithPrefix: (NSString *)prefix;
@end
#import "ArchiveTools.h"

@implementation ArchiveTools
#pragma mark - 归档解档
+ (BOOL)archiveObject:(id)object prefix:(NSString *)prefix {
    if (!object) {
        return NO;
    }
    NSError *error;
    if (@available(iOS 11.0, *)) {
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object
                                             requiringSecureCoding:YES
                                                             error:&error];
        if (error)
            return NO;
        [data writeToFile:[self getPathWithPrefix:prefix] atomically:YES];
     } else {
         NSData*data = [NSKeyedArchiver archivedDataWithRootObject:object];
         [data writeToFile:[self getPathWithPrefix:prefix] atomically:YES];
     }
    return YES;
}
+ (id)unarchiveClass:(Class)class prefix:(NSString *)prefix {
    
    NSError *error;
    NSData *data = [[NSData alloc] initWithContentsOfFile:[self getPathWithPrefix:prefix]];
    //会调用对象的initWithCoder方法
    if (@available(iOS 11.0, *)) {
        id content = [NSKeyedUnarchiver unarchivedObjectOfClass:class fromData:data error:&error];
        if (error) {
            return nil;
        }
        return content;
    } else {
        id content = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        return content;
    }
}
#pragma mark - 存放文件的路径
+ (NSString *)getPathWithPrefix: (NSString *)prefix {
    // document路径
    NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0];
    // 自定义一个文件夹
    NSString *filePathFolder = [documentPath stringsByAppendingPaths:@[@"archiveTemp"]].firstObject;
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePathFolder]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:filePathFolder withIntermediateDirectories:YES attributes:nil error:nil];
    }
    NSString *path = [NSString stringWithFormat:@"%@/%@.archive",filePathFolder,prefix];
    NSLog(@"%@",path);
    return path;
}

3.调用使用

#import "ViewController.h"
#import "Person.h"
#import "ArchiveTools.h"
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *person = [[Person alloc] init];
    person.name = @"I am Lili";
    // 存储
    BOOL isSuccess = [ArchiveTools archiveObject:person prefix:NSStringFromClass(person.class)];
    NSLog(@"啦啦-%@",NSStringFromClass(person.class));
    if (isSuccess) {
        NSLog(@"存储成功");
    } else {
        NSLog(@"存储失败");
    }
    // 读取
    Person  *content = [ArchiveTools unarchiveClass:Person.class prefix:NSStringFromClass(Person.class)];
    NSLog(@"内容是-%@",content);
    NSLog(@"具体-%@", content.name);
    //删除归档文件
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    if ([defaultManager isDeletableFileAtPath:[ArchiveTools getPathWithPrefix:NSStringFromClass(Person.class)]]) {
        [defaultManager removeItemAtPath:[ArchiveTools getPathWithPrefix:NSStringFromClass(Person.class)] error:nil];
    }
}

四、SQLite3

1. 在项目中导入libsqlite3.0.tbd框架
2.使用数据库

#import "ViewController.h"
#import <sqlite3.h>
@interface ViewController ()
@property(nonatomic, assign) int res;

@end

@implementation ViewController
{
    sqlite3 *db; // 声明对象
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self createDB];
    [self createTable];
    [self insertData];
    [self selectData];
    // 关闭数据库
    sqlite3_close(db);
}
#pragma mark - 数据库相关操作
- (void)createDB {
    // 创建数据库路径
    NSString *dbPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)objectAtIndex:0]stringByAppendingPathComponent:@"test.sqlite"];
    NSLog(@"数据库路径-%@",dbPath);
    // 创建或者打开数据库
    const char *p = [dbPath UTF8String];
    int res = sqlite3_open(p, &db);
    self.res = res;
}
// 一、创建表格
- (void)createTable {
    if(self.res == SQLITE_OK) {
        NSLog(@"数据库成功打开");
        NSString *sql = @"create table if not exists temps (t_id integer primary key autoincrement, t_name varchar(20))";
        if ([self execNoQueryWithSQL:sql]) {
            NSLog(@"创建表格成功");
        } else {
            NSLog(@"创建表格失败");
        }
    } else {
        NSLog(@"数据库未正常打开-%d",self.res);
    }
}
// 二、插入数据
- (void)insertData {
    if (self.res == SQLITE_OK) {
        NSString *insert_sql = @"insert into temps (t_name) values('banala')";
        if ([self execNoQueryWithSQL:insert_sql]) {
            NSLog(@"成功插入数据");
        } else {
            NSLog(@"插入数据失败");
        }
    } else {
        NSLog(@"数据库未正常打开-%d",self.res);
    }
}
// 三、删除数据
- (void)deleteData {
    if (self.res == SQLITE_OK) {
        NSString *delete_sql = @"delete from temps where t_id=2";
        if ([self execNoQueryWithSQL:delete_sql]) {
            NSLog(@"成功删除数据");
        } else {
            NSLog(@"删除数据失败");
        }
    } else {
        NSLog(@"数据库未正常打开-%d",self.res);
    }
}
// 四、修改数据
- (void)updataData {
    if (self.res == SQLITE_OK) {
        NSString *update_sql = @"update temps set t_name='ios' where t_id=1";
        if ([self execNoQueryWithSQL:update_sql]) {
            NSLog(@"成功更新数据");
        } else {
            NSLog(@"更新数据失败");
        }
    } else {
        NSLog(@"数据库未正常打开-%d",self.res);
    }
}
// 五、查询简单数据,无参数
- (void)selectData {
    if (self.res == SQLITE_OK) {
//        NSString *select_sql1 = @"select * from temps where t_id=1";
       NSString *select_sql1 = @"select * from temps";
        sqlite3_stmt *stmt1 = [self execQueryWithSQL:select_sql1];
        while (sqlite3_step(stmt1) == SQLITE_ROW) {
            int t_id = sqlite3_column_int(stmt1, 0);
            const unsigned char *t_name = sqlite3_column_text(stmt1, 1);
            NSString *name = [NSString stringWithUTF8String:(char *)t_name];
            NSLog(@"%i %@",t_id,name);
        }
        // 释放stmt statement
        sqlite3_finalize(stmt1);
    } else {
        NSLog(@"数据库未正常打开-%d",self.res);
    }
}
// 六、查询数据2 参数化的sql语句  查找 id>2 并且名字以p开头的 用 ?占位
- (void)selectDataWithParams {
    if (self.res == SQLITE_OK) {
        int seachId2 = 1;
        NSString *seach_name = @"p%";
        NSString *seach_sql = @"select * from temps where t_id>? and t_name like ?";
        sqlite3_stmt *stmt6 = [self execQueryWithSQL:seach_sql andWithParams:@[[NSNumber numberWithInt:seachId2],seach_name]];
        
        //准备执行(相当于点击run query),执行的时候是一行一行的执行
        while (sqlite3_step(stmt6) == SQLITE_ROW) {
            //按照当前列的类型选数据,列数从0开始
            int t_id = sqlite3_column_int(stmt6, 0);
            const unsigned char *t_name = sqlite3_column_text(stmt6, 1);
            NSString *name = [NSString stringWithUTF8String:(char*)t_name];
            NSLog(@"..>>>>>>...%i %@",t_id,name);
        }
        sqlite3_finalize(stmt6);
    } else {
        NSLog(@"数据库未正常打开-%d",self.res);
    }
}

#pragma mark - 执行除查询以外的操作
- (BOOL)execNoQueryWithSQL:(NSString *)sql {
    /*
     执行
     参数1:sqlite3 对象
     参数2:c形式的 sql语句
     参数3:回调函数
     参数4:回调函数的参数
     参数5:错误信息(可以char类型指针接受错误信息,用来查错使用)
     */
    char *error;
    int result =  sqlite3_exec(db, [sql UTF8String], NULL, NULL, &error);
    if (result == SQLITE_OK) {
        return YES;
    } else {
        NSLog(@"%s",error);
    }
    return NO;
}
#pragma mark - 返回查询结果集,无参数
-(sqlite3_stmt *)execQueryWithSQL:(NSString *)sql {
    sqlite3_stmt *stmt;
    /*
     准备执行查询的sql语句 (相当于把查询语句写好)
     参数3:sql语句长度,通常用-1表示(系统会自动计算),也可以用strlength函数计算
     参数4:sql_stmt对象 (执行的对象)
     参数5:未执行的sql语句
     */
    int pre_res = sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, NULL);
    if (pre_res == SQLITE_OK) {
        return stmt;
    }
    return NULL;
}
#pragma mark - 返回查询结果集m,有参数
-(sqlite3_stmt *)execQueryWithSQL:(NSString *)sql andWithParams:(NSArray *)params{
    sqlite3_stmt *stmt;
    int pre_res = sqlite3_prepare_v2(db, [sql UTF8String], -1, &stmt, NULL);
    if (pre_res == SQLITE_OK) {
        if (params!=nil) {
            for (int i = 0 ; i<params.count; i++) {
                id obj = params[i];
                //绑定的数据类型可能为NSString或者NSNumber,或者数据为空,分别判断
                if (obj == nil) {
                    // 数据为空
                    sqlite3_bind_null(stmt, i+1);
                } else if ([obj respondsToSelector:@selector(objCType)]) {
                    //当前的绑定的数据类型位NSNumber
                    //NSNumber判断包装的是int?longInt?shortInt?float?double?
                    /*
                     strstr(参数1,参数2) (strstr() c中函数搜索一个字符串在另一个字符串中的第一次出现,则该函数返回第一次匹配的字符串的地址,找不到返回NULL)
                     判断参数1中的字符在参数2的字符串char*中出现的索引
                     [obj objCType] 如果obj是int返回字符串i
                     */
                    if (strstr("ilsILS", [obj objCType])) {
                        /*
                         绑定参数 如果有where
                         参数1:sqlite_stmt对象 (statement结果集)
                         参数2:占位符索引 从1开始
                         参数3:替代占位符的真实参数
                         */
                        sqlite3_bind_int(stmt, i+1, [obj intValue]);
                    } else if (strstr("fdFD", [obj objCType])){
                        sqlite3_bind_double(stmt, i+1, [obj doubleValue]);
                    } else {
                        stmt = nil;
                    }
                } else if ([obj respondsToSelector:@selector(UTF8String)]) {
                    //当前的绑定的数据类型为NSString 判断是否有UTF8String方法
                    //用bind替换占位符 索引从1开始
                    sqlite3_bind_text(stmt, i+1, [obj UTF8String], -1, NULL);
                } else {
                    stmt = nil;
                }
            }
        }
        return stmt;
    }
    return NULL;
}
@end


五、Core Data

Core Data是iOS5之后才出现的一个框架,提供了直接使用SQLite数据库的大部分灵活性,它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中,也能够将保存在数据库中的数据还原成OC对象,通过CoreData管理应用程序的数据模型,可以极大程度减少需要编写的代码数量!

  • 废话不多说,直接上代码!
//创建数据库
- (void)createSqlite{
    
    //1、创建模型对象
    //获取模型路径
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"CoreData__" withExtension:@"momd"];
    //根据模型文件创建模型对象
    NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    
    
    //2、创建持久化存储助理:数据库
    //利用模型对象创建助理对象
    NSPersistentStoreCoordinator *store = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
    
    //数据库的名称和路径
    NSString *docStr = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *sqlPath = [docStr stringByAppendingPathComponent:@"coreData.sqlite"];
    NSLog(@"数据库 path = %@", sqlPath);
    NSURL *sqlUrl = [NSURL fileURLWithPath:sqlPath];
    
    NSError *error = nil;
    //设置数据库相关信息 添加一个持久化存储库并设置存储类型和路径,NSSQLiteStoreType:SQLite作为存储库
    [store addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:sqlUrl options:nil error:&error];
    
    if (error) {
        NSLog(@"添加数据库失败:%@",error);
    } else {
        NSLog(@"添加数据库成功");
    }
    
    //3、创建上下文 保存信息 操作数据库
    
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
    
    //关联持久化助理
    context.persistentStoreCoordinator = store;
    
    _context = context;
    
    
}

六、Keychain(SQLite API进行封装的库)
  • Keychain存储也并不是绝对安全,越狱设备可以拿到
  • 程序卸载还存在
  • 对于每个应用来说,keychain都有两个访问区,私有区和公共区
    私有区是一个闭合的存储区域,每个应用只能操作自己的私有区,本应用存储的任何数据对其他程序不可见,其他程序也没有权限访问这个私有区。(可以理解为存在钥匙串的沙盒)。
    公共区,apple提供给同一个开发者账号开发的多个app之间的一个数据共享模块。现在只局限于同一个开发者账号下的不同app之间数据共享。这个区域是独立于私有区的另外一个数据存储空间。实现多个应用间共同访问一些数据。
    缺陷:Keychain变化的几种情况
    1.越狱机
    2.部分操作系统bug
    3.应用卸载后升级iOS系统

七、第三方FMDB,BGFMDB
  • 下载FMDB框架,程序导入sqlite3.0框架
  • 废话不多说,上代码
#import "ViewController.h"
#import "FMDB.h"
#import "Person.h"
@interface ViewController ()
{
    FMDatabase *db;
}
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    [self createDB];
    [self deleteData];
    [self queryData];
}

-(void)createDB {
   // 1.
    NSString *docuPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
    NSString *dbPath = [docuPath stringByAppendingPathComponent:@"test.db"];
    NSLog(@"!!!dbPath = %@",dbPath);
    //2.创建对应路径下数据库
    db = [FMDatabase databaseWithPath:dbPath];
    //3.在数据库中进行增删改查操作时,需要判断数据库是否open
    [db open];
    if (![db open]) {
        NSLog(@"db open fail");
        return;
    }
    //4.数据库中创建表(可创建多张)
    NSString *sql = @"create table if not exists t_student ('ID' INTEGER PRIMARY KEY AUTOINCREMENT,'name' TEXT NOT NULL, 'phone' TEXT NOT NULL,'score' INTEGER NOT NULL)";
    //5.执行更新操作 此处database直接操作,不考虑多线程问题,多线程问题,用FMDatabaseQueue 每次数据库操作之后都会返回bool数值,YES,表示success,NO,表示fail,可以通过 @see lastError @see lastErrorCode @see lastErrorMessage
    BOOL result = [db executeUpdate:sql];
    if (result) {
        NSLog(@"create table success");
        
    }
    [db close];
}
- (void)insertData {
    [db open];
    // 插入
    BOOL result2 = [db executeUpdate:@"insert into 't_student'(ID,name,phone,score) values(?,?,?,?)" withArgumentsInArray:@[@113,@"x3",@"13",@53]];
    if (result2) {
        NSLog(@"insert into 't_studet' success");
        [self showAlertWithTitle:@"insert  success" message:nil person:nil];
    } else {
        [self showAlertWithTitle:[db lastError].description message:nil person:nil];
    }
    [db close];
}
-(void)deleteData{
    [db open];
    BOOL result = [db executeUpdate:@"delete from 't_student' where ID = ?" withArgumentsInArray:@[@153]];
    if (result) {
        NSLog(@"delete from 't_student' success");
        [self showAlertWithTitle:@"delete  success" message:nil person:nil];
    } else {
        [self showAlertWithTitle:[db lastError].description message:nil person:nil];
    }
    [db close];
    
}
-(void)updateData{
    [db open];
    BOOL result = [db executeUpdate:@"update 't_student' set ID = ? where name = ?" withArgumentsInArray:@[@153,@"x3"]];
    if (result) {
        NSLog(@"update 't_student' success");
        [self showAlertWithTitle:@"update  success" message:nil person:nil];
    } else {
        [self showAlertWithTitle:[db lastError].description message:nil person:nil];
    }
    [db close];
}
-(void)queryData {
    [db open];
    FMResultSet *result = [db executeQuery:@"select * from 't_student' where ID = ?" withArgumentsInArray:@[@153]];
    NSMutableArray *arr = [NSMutableArray array];
    while ([result next]) {
        Person *person = [Person new];
        person.ID = [result intForColumn:@"ID"];
        person.name = [result stringForColumn:@"name"];
        person.phone = [result stringForColumn:@"phone"];
        person.score = [result intForColumn:@"score"];
        [arr addObject:person];
        NSLog(@"从数据库查询到的人员 %d-%@-%@-%d",person.ID, person.name,person.phone,person.score);
        [self showAlertWithTitle:@"query  success" message:nil person:person];
        
    }
}
-(void)showAlertWithTitle:(NSString *)title
                  message:(NSString *)message
                   person:(Person *)person
{
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"sure" style:UIAlertActionStyleDefault handler:nil];
    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = person.name ? person.name : @"other";
    }];
    [alert addAction:cancelAction];
    [self presentViewController:alert animated:YES completion:^{
      }];
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345