所有的应用必然要包括数据的输入输出、存储等技术。iOS开发也不例外。就iOS而言持久化的手段包括:
对于少量数据有NSUserDefaults、写入相关文件file、归解档archive 、keyChain等技术。
对于大量数据就需要用到SQLite数据库,iOS中内置SQLite数据库提供的是C风格的API,一般都用FMDB来操作数据库。iOS还提供了CoreData,它提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite数据库文件中,也能够将保存在数据库中的数据还原成OC对象。另外还有就是Realm,Realm也是对象-关系映射(ORM),它有一套自己的数据库存储引擎,比sqlite更轻量级,拥有更快的速度,并且具有很多现代数据库的特性,比如支持JSON,流式api,数据变更通知,自动数据同步,简单身份验证,访问控制,事件处理,最重要的是跨平台,目前已有Java,Objective C,Swift,js,.NET等实现。
沙盒
为了提供更加安全机制,iOS提供了应用程序沙盒机制,保证每个应用程序只能访问本沙盒内的数据。打开模拟器中程序的相应文件夹,可以看到以下文件结构:
- Documents:除了基于NSUserDefaults的首选设置以外,应用程序的数据、文件都保存在改目录下。
- Library:基于NSUserDefaults的首选参数保存在Library/Preferences目录下。
- tmp:该目录供应用程序存储临时文件。
获取Documents目录
NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *document = [pathArr objectAtIndex:0];
获取tmp目录
NSString *tmpPath = NSTemporaryDirectory();
NSUserDefaults保存与读取数据
//保存
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:@{@"name":@"diyinqianchang",@"age":@"18"} forKey:@"save"];
[defaults synchronize];
//读取
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
id info = [defaults objectForKey:@"save"];
if (info) {
NSLog(@"%@",info);
}
writeToFiled方法
NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *document = [pathArr objectAtIndex:0];
NSString *path = [NSString stringWithFormat:@"%@/myList.plist",document];
NSArray *dataArr = [NSArray arrayWithObjects:@"name",@"age", nil];
[dataArr writeToFile:path atomically:YES];
其他的类似
对象归档保存自定义的类
使用NSKeyedArchiver和NSKeyedUnarchiver进行归解档非常简单。归解档自定义对象,需要此对象遵守NSCoding协议。
.h
#import
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property(nonatomic,strong)NSString *name;
@property(nonatomic,assign)NSInteger age;
-(instancetype)initWithName:(NSString *)name age:(NSInteger)age;
@end
NS_ASSUME_NONNULL_END
.m
#import "Person.h"
@implementation Person
-(instancetype)initWithName:(NSString *)name age:(NSInteger)age
{
self = [super init];
if (self) {
_name = name;
_age = age;
}
return self;
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
_name = [aDecoder decodeObjectForKey:@"name"];
_age = [aDecoder decodeIntegerForKey:@"age"];
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:_name forKey:@"name"];
[aCoder encodeInteger:_age forKey:@"age"];
}
@end
//归档
Person *person = [[Person alloc] initWithName:@"diyinqianchang" age:18];
[NSKeyedArchiver archiveRootObject:person toFile:@"person.archive"];
//解档
Person *p = [NSKeyedUnarchiver unarchiveObjectWithFile:@"person.archive"];
注意对象嵌套对象的情况。
钥匙串存储
保存在Keychain中的信息即使当应用从设备山卸载后也不会消失,Keychain信息甚至可以在同一开发者的多个应用间共享。Keychain内部可以保存很多的信息。每条信息作为一个单独的keychain item,每个item包含一个data和多个可见的属性。这些属性用来描述data访问、搜索等属性。
- SecItemAdd 添加一个keychain item
- SecItemUpdate 修改一个keychain item
- SecItemCopyMatching 搜索一个keychain item
- SecItemDelete 删除一个keychain item
-(NSMutableDictionary *)getKeychainQuery:(NSString *)service
{
return [NSMutableDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassGenericPassword, (id)kSecClass,
service, (id)kSecAttrService,
service, (id)kSecAttrAccount,
(id)kSecAttrAccessibleAfterFirstUnlock, (id)kSecAttrAccessible,nil];
}
- (BOOL)createKeychainValue:(NSString *)psd forIdentifier:(NSString *)service {
NSMutableDictionary *dictionary = [self getKeychainQuery: service];
//非常值得注意的事kSecValueData字段只接受UTF8格式的 NSData *类型,否则addItem/updateItem就会crash,并且一定记得带上service和account字段
NSData *psdData = [psd dataUsingEncoding:NSUTF8StringEncoding];
[dictionary setObject:passwordData forKey:(id)kSecValueData];
OSStatus status = SecItemAdd((CFDictionaryRef)dictionary, NULL);
if (status == errSecSuccess) {
return YES;
}
return NO;
}
- (void)deleteKeychainValue:(NSString *)service {
NSMutableDictionary *dictionary = [self getKeychainQuery:service];
SecItemDelete((CFDictionaryRef)dictionary);
}
- (NSData *)searchKeychainCopyMatching:(NSString *)service {
NSMutableDictionary *searchDictionary = [self getKeychainQuery:service];
//只返回搜索到的第一条item,这个是搜索条件。
[searchDictionary setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
//返回item的kSecValueData 字段。也就是我们一般用于存放的密码,返回类型为NSData *类型
[searchDictionary setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
//我来解释下这里匹配出的是 找到一条符合ksecAttrAccount、类型为普通密码类型kSecClass,返回ksecValueData字段。
NSData *result = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)searchDictionary,
(CFTypeRef *)&result);
[searchDictionary release];
return result;
}
- (BOOL)updateKeychainValue:(NSString *)password forIdentifier:(NSString *)service {
NSMutableDictionary *searchDictionary = [self getKeychainQuery:service];
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
[updateDictionary setObject:passwordData forKey:(id)kSecValueData];
OSStatus status = SecItemUpdate((CFDictionaryRef)searchDictionary,
(CFDictionaryRef)updateDictionary);
if (status == errSecSuccess) {
return YES;
}
return NO;
}