iOS-保存数据

技 术 文 章 / 超 人


沙盒中持久化保存数据方案

从简单到复杂

沙盒说明

  • 每个iOS应用在iOS手机中都拥有一个独立的沙盒目录(类似于windows电脑系统里的软件。每个软件都放在自己的文件夹中)。可以用NSHomeDirectory()来获取当前应用的沙盒目录 。

  • 每个沙盒目录中都有3个固定文件夹DocumentsLibrarytmp 。在Xcode5以下多一个项目名字.app的文件,而Xcode6 中被删除了(Xcode5Xcode6应用在模拟器中的沙盒路径也不同)。

    Documents、Library、tmp

  • 沙盒文件夹说明:
    * Documents :保存用户产生的数据,指用户在使用当前应用时保存的一些数据,比如保存在应用中的图片、保存下载的文件等。iTunes同步设备的时候会备份该目录。获取Documents路径方法NSHomeDirectory();

NSSearchPathForDirectoriesInDomains方法说明,用来查找文件路径
参数1:指定收索路径名称
typedef NS_ENUM(NSUInteger, NSSearchPathDirectory) {
NSApplicationDirectory = 1, // supported applications (Applications)
NSDemoApplicationDirectory, // unsupported applications, demonstration versions (Demos)
NSDeveloperApplicationDirectory, // developer applications (Developer/Applications). DEPRECATED - there is no one single Developer directory.
NSAdminApplicationDirectory, // system and network administration applications (Administration)
NSLibraryDirectory, // various documentation, support, and configuration files, resources (Library)
NSDeveloperDirectory, // developer resources (Developer) DEPRECATED - there is no one single Developer directory.
NSUserDirectory, // user home directories (Users)
NSDocumentationDirectory, // documentation (Documentation)
NSDocumentDirectory, // documents (Documents)
NSCoreServiceDirectory, // location of CoreServices directory (System/Library/CoreServices)
NSAutosavedInformationDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 11, // location of autosaved documents (Documents/Autosaved)
NSDesktopDirectory = 12, // location of user's desktop
NSCachesDirectory = 13, // location of discardable cache files (Library/Caches)
NSApplicationSupportDirectory = 14, // location of application support files (plug-ins, etc) (Library/Application Support)
NSDownloadsDirectory API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 15, // location of the user's "Downloads" directory
NSInputMethodsDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 16, // input methods (Library/Input Methods)
NSMoviesDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 17, // location of user's Movies directory (~/Movies)
NSMusicDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 18, // location of user's Music directory (~/Music)
NSPicturesDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 19, // location of user's Pictures directory (~/Pictures)
NSPrinterDescriptionDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 20, // location of system's PPDs directory (Library/Printers/PPDs)
NSSharedPublicDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 21, // location of user's Public sharing directory (~/Public)
NSPreferencePanesDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 22, // location of the PreferencePanes directory for use with System Preferences (Library/PreferencePanes)
NSApplicationScriptsDirectory NS_ENUM_AVAILABLE(10_8, NA) = 23, // location of the user scripts folder for the calling application (~/Library/Application Scripts/code-signing-id)
NSItemReplacementDirectory API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)) = 99, // For use with NSFileManager's URLForDirectory:inDomain:appropriateForURL:create:error:
NSAllApplicationsDirectory = 100, // all directories where applications can occur
NSAllLibrariesDirectory = 101, // all directories where resources can occur
NSTrashDirectory API_AVAILABLE(macos(10.8), ios(11.0)) API_UNAVAILABLE(watchos, tvos) = 102 // location of Trash directory
};
参数2:查找的目录类型
enum {
NSUserDomainMask = 1, ///< 用户主目录
NSLocalDomainMask = 2, ///< 当前机器
NSNetworkDomainMask = 4, ///< 网络中可见的主机
NSSystemDomainMask = 8, ///< 系统目录,不可修改(/System)
NSAllDomainsMask = 0x0ffff, ///< 所有
};
参数3:是否显示完整路径,YES为展开后完整路径

//获取Documents路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsPath = [paths objectAtIndex:0];

*  Library :

//获取Library路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryPath = [paths objectAtIndex:0];

**  Caches:保存应用运行时生成的需要持久化的数据,比如SDWebImage把缓存的图片就存放到该目录下。当程序退出后,该目录保存的文件一直存在。一般存储体积大、不需要备份的非重要数据。iTunes同步设备时不会备份该目录。

//获取Caches路径
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachesPath = [paths objectAtIndex:0];

**  Preferences:保存应用的所有偏好设置,iOSSettings(设置)应用会在该目录中查找应用的设置信息。iTunes同步设备时会备份该目录

*  tmp :保存应用运行时所需的临时数据,使用完毕后再将相应的文件从该目录删除。当手机内存爆满时,就算应用没有运行时,系统会清除所有应用该目录下的文件。iTunes同步设备时不会备份该目录。获取tmp路径方法NSTemporaryDirectory();


1. NSUserDefaults(最简单)

  • NSUserDefaults 是一个单例对象,在整个应用程序的生命周期中都只有一个实例。NSUserDefaults本身也是用.plist保存的,系统把它保存在Libaray目录下的Preference文件夹中。
  • NSUserDefaults保存的数据类型有:NSNumber, 基本数据类型(intNSInter,float,double,CGFlat......), NSString, NSData, NSArray, NSDictionary, NSURL
  • NSUserDefaults一般保存配置信息,比如用户名密码、是否保存用户名和密码、是否离线下载等一些配置条件信息
    基本用法:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
//保存值(key值同名的时候会覆盖)  
[defaults setObject:@"用户名" forKey:@"Key名称"];
//立即保存
[defaults synchronize];
//取值
NSString *username = [defaults objectForKey:@"Key名称];
同样,保存还有一些方法,比如:
//保存NSInteger
[defaults setInteger:(NSInteger) forKey:(nonnull NSString *)];
//保存BOOL
[defaults setBool:(BOOL) forKey:(nonnull NSString *)];
//保存NSURL
[defaults setURL:(nullable NSURL *) forKey:(nonnull NSString *)];
//保存float
[defaults setFloat:(float) forKey:(nonnull NSString *)];
//保存double
[defaults setDouble:(double) forKey:(nonnull NSString *)];
取值另外方法:
//取值
[defaults integerForKey:(nonnull NSString *)];
[defaults boolForKey:(nonnull NSString *)];
[defaults URLForKey:(nonnull NSString *)];
[defaults floatForKey:(nonnull NSString *)];
[defaults doubleForKey:(nonnull NSString *)];
删除方法:
//删除指定key的数据
[defaults removeObjectForKey:(nonnull NSString *)];

synchronize。立即保存,苹果文档这么说:

Writes any modifications to the persistent domains to disk and updates all unmodified persistent domains to what is on disk.
Return Value YES if the data was saved successfully to disk, otherwise NO.(大概意思是,使用synchronize后会立即把所有执行了存值方法但未在内存中修改值的值修改。该方法返回YES表示修改成功,NO表示修改失败)


2. plist文件保存(简单实用)

其实NSUserDefaults本身也是保存的一份plist文件在Library下的Preferences文件夹中

  • 一般在iOSplist保存,plist本身就是XML文件,名字后缀为.plist
    plist主要保存的数据类型为NSStringNSNumberNSDataNSArrayNSDictionaryNSMutableArrayNSMutableDictionary
  • 具体实现:
/* 例如保存在Documents路径下 */
//Documents文件路径
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
//拼接Documents+文件名,plistName就是文件名字。比如@"data.plist"
NSString *filePath = [self.path stringByAppendingPathComponent:plistName];
//把字典写入到plist文件,比如文件path为:~/Documents/data.plist
[dictionary writeToFile:path atomically:YES];
//把数组写入到plist文件中
[array writeToFile:path atomically:YES];
  • 读取数据
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfURL:[NSURL fileURLWithPath:(nonnull NSString *)]];
NSDictionary *dictionary =  [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:(nullable NSString *) ofType:(nullable NSString *)]];
NSArray *array = [NSArray arrayWithContentsOfURL:[NSURL fileURLWithPath:(nonnull NSString *)]];
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:(nullable NSString *) ofType:(nullable NSString *)]];

3. 归档(序列化)

  • 一般保存自定义的对象(例如归档聊天历史数据或者状态数据),但是只有遵守NSCoding的类才能只用归档。
  • 准守NSCoding协议必须要实现以下两个require方法:
//归档会触发
(void)encodeWithCoder:(NSCoder *)aCoder 
//解归
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder

档会触发

  • Coding类具体实现:
@interface Coding : NSObject<NSCoding>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
#import "ModelClass.h"
//必须导入
#import <objc/runtime.h>
@implementation Coding
 /**
 *  根据类动画获取类的所有属性
 *
 *  @param cls <#cls description#>
 *
 *  @return <#return value description#>
 */
- (NSArray *)perperiesWithClass:(Class)cls
{
    NSMutableArray *perperies = [NSMutableArray array];
    //定义属性数量
    unsigned int outCount;
    //动态获取cls类的所有属性,并把值叠加到outCount,每有一个属性累加1
    objc_property_t *properties = class_copyPropertyList(cls, &outCount);
    //遍历cls类的所有属性
    for (int i = 0; i < outCount; i++)
    {
        objc_property_t property = properties[i];
        const char *name = property_getName(property);
        NSString *s = [[NSString alloc] initWithUTF8String:name];
        [perperies addObject:s];
    }
    return perperies;
}


/**
 *  归档会触发
 *
 *  @param aCoder <#aCoder description#>
 */
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    for (NSString *perperty in [self perperiesWithClass:[self class]])
    {
        [aCoder encodeObject:perperty forKey:perperty];
    }
}


/**
 *  解归档会触发
 *
 *  @param aDecoder <#aDecoder description#>
 *
 *  @return <#return value description#>
 */
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init])
    {
        for (NSString *perperty in [self perperiesWithClass:[self class]])
        {
            [self setValue:[aDecoder decodeObjectForKey:perperty] forKey:perperty];;
        }
    }
    return self;
}
@end
  • 归档具体使用:
//创建需要归档的数据
 ModelClass *model1 = [ModelClass new];
 model1.name = @"用户名1";
 model.password = @"****";
 model1.age = 18;
//创建需要归档的数据
 ModelClass *model2 = [ModelClass new];
 model2.name = @"用户名2";
 mode2.password = @"****";
 model2.age = 20;

 NSArray *array = @[model1, model2];

 /* 保存对象转化为二进制数据(一定是可变对象)归档的data和解档的data必须是同一个对象。*/
 NSMutableData *data = [NSMutableData data];

/* 归档 */
 //1.初始化
 NSKeyedArchiver *archivier = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
 //2.归档
 [archivier encodeObject:array forKey:@"key"];
 //3.完成归档
 [archivier finishEncoding];
/* 归档 */


 //1.初始化解归档对象,解档的data跟归档data是同一个对象
 NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
 //2.解归档
 NSArray *persons = [unarchiver decodeObjectForKey:@"key"];
 //3.完成解归档
 [unarchiver finishDecoding];

4. CoreData

下面是CoreData堆栈图

image.png

适用于保存用户数据,聊天数据等比较复杂数量较多属性较多的数据

  • CoreData是iOS5时苹果推出的,实际时对SQL的一种封装

  • 提供了一种“对象-关系映射”的功能,能将OC对象转化成数据,保存Sqlite中。

  • CoreData的好处就是能够合理管理内存,避免sql语句的麻烦(不用写sql语句)。

构成

  • NSManagedObjectContext:
    被管理的数据上下文,主要作用:插入、查询、删除。
  • NSManagedObjectModel:
    数据库所有的表结构和数据结构,包含各个实体的定义的信息。主要作用就是添加实体、实体属性,建立属性之间的关系。
  • NSPersistentStoreCoordinator
    持久化存储助理对象,相当于数据库的连接器。主要作用就是设置存储的名字、位置、存储方式。
  • NSFetchRequest
    相当于select语句。查询封装对象。
  • NSEntityDescription
    实体结构对象,相当于表格结构。后缀为xxx.xcdatamodeld文件,编译后为xxx.momd的文件。

创建工程的时候,勾上Use Core Data
。如图所示:

勾选Use Core Data会自动生成CoreData文件
  • 勾选Use Core Data后,项目的AppDelegate文件中已经生成好了相应CoreData代码
#import <CoreData/CoreData.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>@property (strong, nonatomic) UIWindow *window;
/* 上下文 */
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
/* 管理数据模型*/
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
/*持久化的数据的对象*/
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
/** * 保存上下文 */
- (void)saveContext;
/** * 获取documents路径 
*
* @return <#return value description#> 
*/
- (NSURL *)applicationDocumentsDirectory;
@end
  • 创建实体对象,并添加属性。


    Core Data实体对象解析图
  • 实体结构

创建模型。选择项目->右击->New File...->iOS 下面的Core Data->NSManagedObject subclass->选择CoreData文件点击Next->选择要生成的对象模型,点击Next->Create。具体操作如下截图:

  • 创建模型
  • 创建类
  • 选择要生成的CoreData文件
  • 选择要生成的对象
  • 生成好的模型
    具体实现代码如下:
    * 保存数据
- (NSManagedObjectContext *)context
{ 
AppDelegate *app = [UIApplication sharedApplication].delegate; 
return app.managedObjectContext;
}
//创建Person对象
/*insertNewObjectForEntityForName:就是创建的实体名字。inManagedObjectContext:上下文,appDelegate里面已经创建完成。*/ 
Person *person = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:[self context]];
 //赋值 
[person setValue:@"小王" forKey:@"name"];
 [person setValue:@(35) forKey:@"age"]; 
//保存 
if (![[self context] save:nil]) 
{ 
    //TODO:保存失败
 }

* 查询

//创建查询对象
 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
#if 0 
//条件查询 //
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<=35"]; 
//查询名字带有王的 
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name like[cd]'*王*'"];
//设置查询条件 request.predicate = predicate;
#endif 
//排序 NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"age" ascending:NO]; 
//设置排序条件 
request.sortDescriptors = @[sort]; 
//执行查询 
NSArray *objectArray = [[self context] executeFetchRequest:request error:nil];
 //遍历查询结果 
for (Person *p in objectArray)
 {
 NSLog(@"%@ - %@",[p valueForKey:@"name"],[p valueForKey:@"age"]);
 }

* 修改

//先查询要修改的对象 
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
 //设置查询条件 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name='小王' and age = 35"]; 
request.predicate = predicate;
 //执行查询
 NSArray *objectArray = [[self context] executeFetchRequest:request error:nil]; 
//遍历要修改的对象
 for (Person *p in objectArray) 
{
 //修改(修改内存数据,没有同步数据库)
 [p setValue:@(45) forKey:@"age"]; 
} 
//同步数据库 [[self context] save:nil];

* 删除

//查询要删除的数据
 NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
 //设置查询条件 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name='小王'"];
 request.predicate = predicate;
 //执行查询
 NSArray *objectArray = [[self context] executeFetchRequest:request error:nil]; 
//遍历删除 for (Person *p in objectArray) 
{
 //删除内存中的数据 [[self context] deleteObject:p];
 }
 //同步数据库 [[self context] save:nil];

CoreData最重要的是关系,由于时间关系。当app更新版本,并且表结构有修改,需要版本升级和数据迁移操作,否则app就是崩掉。


4. 数据库

  • iOS用的sqlite3, 使用sqlite3需要配置库文件libsqlite3.tbd或者导入libsqlite3.0.tbd,这两个库导入任何一个都可以,如图所示。
  • 导入库
    保存大量数据可以优先考虑用数据库,sql语句对查询操作有优化作用,所以从查询速度或者插入效率都是很高的。

首先需要对常用的sql语句了解。这里就不在介绍了,可以看下这个教程:http://www.w3school.com.cn/sql/

  • sqlite使用步骤:
    *指定数据库路径。
    *创建sqlite3
    *对象并且打开数据库。
    *创建表。
    *对数据库操作,包括增删改查。
    *关闭数据库。

  • 具体实现:
    *数据库路径

//返回数据库路径,保存到Cache目录下
-(NSString *)databasePath{ 
NSString *path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; 
return [path stringByAppendingPathComponent:@"contacts.db"];
}

*创建sqlite3
对象并且打开数据库,如果数据库打开成功,就创建表。

//数据库对象 sqlite3 *contactDB;
const char *path = [[self databasePath] UTF8String];
 if (sqlite3_open(path, &contactDB) == SQLITE_OK) 
{ 
char *errMsg; const char *sql_stmt = "CREATE TABLE IF NOT EXISTS CONTACTS(ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT, ADDRESS TEXT,PHONE TEXT)"; 
//执行语句 
    if (sqlite3_exec(contactDB, sql_stmt, NULL, NULL, &errMsg) !=   
  SQLITE_OK) {
 //创建表失败 
    }
} 
else
 {
 //打开数据库失败
 } sqlite3_close(contactDB);

*代码解释:
sqlite3_open
打开指定路径的数据库,如果数据库不存在,就会创建一个新的数据库。
SQLITE_OK
是一个常量,表示打开数据库成功。下面是苹果的定义:

define SQLITE_OK 0 /* Successful result */

& contactDB:就是数据库对象。
& sqlite3_exec:就是执行sql语句方法。
& sqlite3_close:关闭数据库,一般暂时不用数据库的时候手动关闭,防止资源浪费。

*保存数据到数据库

//是一个抽象类型,是一个句柄,在使用过程中一般以它的指针进行操作sqlite3_stmt *statement;
//数据库路径
const char *path = [[self databasePath] UTF8String];
//使用的时候打开数据库
if (sqlite3_open(path, &contactDB) == SQLITE_OK)
{
 NSString *insertSQL = [NSString stringWithFormat:@"INSERT INTO CONTACTS (name,address,phone) VALUES(\"%@\",\"%@\",\"%@\")",name.text,address.text,phone.text]; 
const char *insert_stmt = [insertSQL UTF8String]; 
// 这个函数将sql文本转换成一个准备语句(prepared statement)对象,同时返回这个对象的指针。这个接口需要一个数据库连接指针以及一个要准备的包含SQL语句的文本。它实际上并不执行这个SQL语句,它仅仅为执行准备这个sql语句 sqlite3_prepare_v2(contactDB, insert_stmt, -1, &statement, NULL); 
//执行这个sql 
if (sqlite3_step(statement) == SQLITE_DONE) 
{ 
//TODO:已存储到数据库; 
} else
 { 
//TODO:保存失败
 } 
//销毁statement对象 sqlite3_finalize(statement); //关闭数据库 
sqlite3_close(contactDB);
}

*查询操作

//数据库路径
const char *path = [[self databasePath] UTF8String];
//查询结果集对象句柄sqlite3_stmt *statement;
//打开数据库
if (sqlite3_open(path, &contactDB) == SQLITE_OK)
{ 
//查询的sql语句 
NSString *querySQL = [NSString stringWithFormat:@"SELECT address,phone from contacts where name=\"%@\"",name.text]; 
const char *query_stmt = [querySQL UTF8String]; 
//执行查询sql语句 
if (sqlite3_prepare_v2(contactDB, query_stmt, -1, &statement, NULL) == SQLITE_OK) 
{
 //遍历每条数据 
if (sqlite3_step(statement) == SQLITE_ROW) 
{ 
//获取每条数据的字段。
 NSString *addressField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 0)]; 
address.text = addressField; NSString *phoneField = [[NSString alloc] initWithUTF8String:(const char *)sqlite3_column_text(statement, 1 )]; 
phone.text = phoneField; 
//TODO:已查到结果 
} else 
{ 
//TODO:未查到结果 
} 
sqlite3_finalize(statement);
 } sqlite3_close(contactDB);
}

沙盒外持久化保存数据方案

1. SSKeyChain

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

推荐阅读更多精彩内容

  • 概论 所谓的持久化,就是将数据保存到硬盘中,使得在应用程序或机器重启后可以继续访问之前保存的数据。在iOS开发中,...
    Leeson1989阅读 1,905评论 4 1
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 应用沙盒 每个iOS应用都有自己的应用沙盒(应用沙盒就是文件系统目录),与其他文件系统隔离。应用必须待在自己的沙盒...
    iOS_程序猿阅读 1,101评论 0 6
  • 永远记住自己想要的是什么 不心动 不开始 尽管碰到那个心动的人很难 但记住 上天会给你本应属于你的东西 在没有...
    七哥sunny_阅读 212评论 0 2
  • 这是android伸手党知识收集系列的第六篇,来整理android开发进阶相关知识:Window,View,事件分...
    passiontim阅读 15,265评论 10 150