意义
缓存的意义不必多说,在离线使用场景下让用户使用,为用户提供更好的体验,也可以提高活跃度,方便产品分析离线数据。
分类
iOS的缓存,大概有这么几种,NSUserDefaults,归档和接档,keychain,文件操作,SQLite,Coredata,Realm;接下来会分类介绍一下。
使用
1.NSUserDefaults的使用和总结
这是苹果为我们提供好的缓存策略,存储的时候以键值对key-value的形式存储,读取的时候通过key来读取,和我们熟悉的字典操作一样。下面看一段代码:
//保存
+(void)saveValue:(NSObject *)value forKey:(NSString *)key{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
}
//读取
+(NSObject *)getValueForKey:(NSString *)key{
return [[NSUserDefaults standardUserDefaults] objectForKey:key];
}
// NSUserDefaults 使用
NSDictionary *dict = @{
@"name":@"jiang",
@"age":@(26)};
[UserDefaultsHandle saveValue:dict forKey:@"userInfo"];
NSLog(@"userInfo:%@",[UserDefaultsHandle getValueForKey:@"userInfo"]);
NSUserDefaults不能存储没有遵守NSCoding协议的对象,所以如果我们想存储自己创建的对象,需要手动实现NSCoding的两个协议。
// 归档方法
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeObject:self.age forKey:@"age"];
[aCoder encodeObject:self.gender forKey:@"gender"];
}
// 反归档方法
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self != nil) {
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeObjectForKey:@"age"];
self.gender = [aDecoder decodeObjectForKey:@"gender"];
}
return self;
}
//使用
Person *person = [Person new];
person.name = @"屾铭";
person.age = @(22);
person.gender = @"男";
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:person];
[UserDefaultsHandle saveValue:encodedObject forKey:@"personInfo"];
NSLog(@"personInfo:%@",[NSKeyedUnarchiver unarchiveObjectWithData:(NSData *)[UserDefaultsHandle getValueForKey:@"personInfo"]]);
2.归档和解档配合文件使用
//解档 归档 配合 文件使用
Person *person = [Person new];
person.name = @"屾铭";
person.age = @(22);
person.gender = @"男";
NSData *encodedObject = [NSKeyedArchiver archivedDataWithRootObject:person];
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [documentPath stringByAppendingPathComponent:@"archiverFile"];
BOOL result = [NSKeyedArchiver archiveRootObject:encodedObject toFile:filePath];
if (result) {
NSLog(@"归档成功:%@",filePath);
}else
{
NSLog(@"归档失败");
}
Person *personOne = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedUnarchiver unarchiveObjectWithFile:filePath]];
NSLog(@"%@",personOne);
3.Sqllite的使用
初始化数据库和创建表
//数据库初始化
+(SQLLiteHandle *)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *dbPath = [(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES))[0] stringByAppendingPathComponent:@"sqlLite.db"];
_dataBase = [SQLLiteHandle databaseQueueWithPath:dbPath];
});
return _dataBase;
}
//创建表
-(void)creatTable:(NSString *)sqlString{
[_dataBase inDatabase:^(FMDatabase *db) {
BOOL status = [db executeUpdate:sqlString];
if (status) {
NSLog(@"create table success");
}else{
NSLog(@"create table fail");
}
}];
}
[[SQLLiteHandle shareInstance] creatTable:@"CREATE TABLE IF NOT EXISTS staff (staffId integer PRIMARY KEY NOT NULL,name text(128),gender text(128),age integer(128),department text(128))"];
增删改查的使用
//插入
-(BOOL)insertDataWithSqlString:(NSDictionary *)dict{
__block BOOL status = NO;
[_dataBase inDatabase:^(FMDatabase *db) {
status = [db executeUpdate:@"INSERT or replace INTO staff (staffId,name,gender,age,department) VALUES (?,?,?,?,?)",dict[@"staffId"],dict[@"name"],dict[@"gender"],dict[@"age"],dict[@"department"]];
}];
if (status) {
NSLog(@"insert success!");
}else{
NSLog(@"insert fail!");
}
return status;
}
NSDictionary *dict = @{@"staffId":@(12309),
@"name":@"niu",
@"gender":@"似男非女",
@"age":@(26),
@"department":@"技术部"
};
[[SQLLiteHandle shareInstance] insertDataWithSqlString:dict];
//查询
-(NSDictionary *)selectStaffWithStaffId:(NSString *)staffId{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[_dataBase inDatabase:^(FMDatabase *db) {
NSString *selectString = [NSString stringWithFormat:@"SELECT * FROM staff WHERE staffId = '%@'",staffId];
FMResultSet *result = [db executeQuery:selectString];
while ([result next]) {
[dict setObject:result[@"staffId"] forKey:@"staffId"];
[dict setObject:result[@"name"] forKey:@"name"];
[dict setObject:result[@"gender"] forKey:@"gender"];
[dict setObject:result[@"age"] forKey:@"age"];
[dict setObject:result[@"department"] forKey:@"department"];
}
[result close];
}];
return dict;
}
NSDictionary *dict = [[SQLLiteHandle shareInstance] selectStaffWithStaffId:@"12309"];
if (dict) {
NSLog(@"%@",dict);
}
//更新
-(BOOL)updateDataWithSqlString:(NSString *)staffId
age:(NSNumber *)age{
__block BOOL status = NO;
[_dataBase inDatabase:^(FMDatabase *db) {
NSString *updateString = [NSString stringWithFormat:@"UPDATE staff SET age = '%@' WHERE staffId = '%@' ",age,staffId];
status = [db executeUpdate:updateString];
}];
if (status) {
NSLog(@"update success!");
}else{
NSLog(@"update fail!");
}
return status;
}
[[SQLLiteHandle shareInstance] updateDataWithSqlString:@"12309" age:@(30)];
//删除
-(BOOL)deleteDataWithstaffId:(NSString *)staffId{
__block BOOL status = NO;
[_dataBase inDatabase:^(FMDatabase *db) {
NSString *updateString = [NSString stringWithFormat:@"DELETE FROM staff WHERE staffId = '%@' ",staffId];
status = [db executeUpdate:updateString];
}];
if (status) {
NSLog(@"delete success!");
}else{
NSLog(@"delete fail!");
}
return status;
}
[[SQLLiteHandle shareInstance] deleteDataWithstaffId:@"12309"];
4.CoreData的使用
//配置
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// 对Magical Record的初始化
[MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:@"Model.sqlite"];
return YES;
}
+(void)createStudent:(NSDictionary *)dict{
UIApplication *application = [UIApplication sharedApplication];
__block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
//线程安全
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Student *localStudent = [Student MR_createEntityInContext:localContext];
localStudent.studentId = 12306;
localStudent.name = @"卖票小学生";
localStudent.age = 66;
localStudent.classNumber = 3;
localStudent.gender = YES;
} completion:^(BOOL success, NSError *error) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
if (success) {
NSLog(@"save success");
}
}];
}
+(void)SelectStudentWith:(NSString *)studentId{
Student *student = [Student MR_findFirstByAttribute:@"studentId" withValue:@(12306)];
NSLog(@"%@",student.name);
}
+(void)updateStudentWithId:(NSString *)studentId
name:(NSString *)name{
Student *student = [Student MR_findFirstByAttribute:@"studentId" withValue:@(12306)];
//线程安全
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Student *localStudent = [student MR_inContext:localContext];
localStudent.name = @"卖票的博士";
} completion:^(BOOL success, NSError *error) {
if (success) {
NSArray *array = [Student MR_findAll];
NSLog(@"%@",array);
}
}];
}
+(void)deleteStudent:(NSString *)studentId{
Student *student = [Student MR_findFirstByAttribute:@"studentId" withValue:@(12306)];
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
Student *localStudent = [student MR_inContext:localContext];
[localStudent MR_deleteEntityInContext:localContext];
} completion:^(BOOL success, NSError *error) {
if (success) {
NSArray *array = [Student MR_findAll];
NSLog(@"%@",array);
}
}];
}
总结
1.简单说一下总结,NSUserDefaults属于轻量级的,存储一些轻量的数据,就是模型不会很复杂的数据,比如单个的属性什么的,NSUserDefaults本身也是一个Plist文件,而Pilst文件有xml格式的。
2.文件的操作会遍布APP的始终,要对苹果给我们的几个沙盒目录熟悉,每个目录的只能要干什么,这又可以开一篇博客了。
3.Sqlite是一种轻便关系型数据库,而且是一种跨平台的,开发之前可以共用一套表机构,和SQL语句逻辑,而且SQL有着悠久的历史,查询效率较高,这是我比较推荐的数据库选择。我在代码中直接用了FMDB来写Demo,为了兼容多线程直接用了Queue来演示。
4.CoreData是苹果在Sqlite上封装了一层,提供给我们的缓存机制,它是基于O-R-M的,可以直接操作对象,但是由于它在SQL基础上封装了一层,所以会存在效率的问题,在实体之间建立关联的时候会遇到崩溃(时间久了,无法Debug和回忆具体的过程了,见谅),不是很推荐使用,代码中为了方便在子线程中操作,直接用了MagicalRecord来演示。
5.Realm敬请期待...
欢迎大家讨论。。。---Jiang