iOS的数据存储是iOS应用开发的重要知识点:
关于这方面知识,网上有很多介绍,但对于代码层次的使用方式并未有系统全面介绍。此文章针对iOS稍熟悉的童鞋,需要对CoreData的原理有一定的了解。目前存储方式大概有以下几种:
- NSKeyedArchiver 适用简单数据加密
- NSUserDefaults 适用配置参数
- Write 文件操作,同NSKeyedArchiver
- SQLite3 操作较复杂,不建议使用。
- CoreData 取代SQLite3,但要遵循NSManagedObjectContext基本规则。
UT代码确保使用方式正确。请阅读以下内容:
#import <XCTest/XCTest.h>
#import <sqlite3.h>
#import "CoreDataHelper.h"
@interface StudyUITests : XCTestCase
@end
@implementation StudyUITests
- (void)setUp
{
[super setUp];
}
- (void)tearDown
{
[super tearDown];
}
-(void)testCoreData{
CoreDataHelper *coreHelper = [[CoreDataHelper alloc] init];
[coreHelper coreDataTest];
}
-(void)testSqlite3{
sqlite3 *db;
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *databasePath = [documentPath stringByAppendingPathComponent:@"test.sqlite"];
NSLog(@"db path: %@",databasePath);
if(sqlite3_open([databasePath UTF8String], &db) != SQLITE_OK){
sqlite3_close(db);
NSLog(@"open sqlite fail!");
NSAssert(FALSE, @"create sqlite error");
}
NSString *sqlCreateTable = @"CREATE TABLE IF NOT EXISTS ptable (ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, address TEXT)";
char *error;
if(sqlite3_exec(db, [sqlCreateTable UTF8String], NULL, NULL, &error) != SQLITE_OK){
sqlite3_close(db);
NSLog(@"can not create table.");
NSAssert(FALSE, @"create table error");
}
NSString *deleteAllSql = @"delete from ptable";
if(sqlite3_exec(db, [deleteAllSql UTF8String], NULL, NULL, &error) != SQLITE_OK){
sqlite3_close(db);
NSLog(@"delete values from table");
NSAssert(FALSE, @"delete values from table");
}
NSString *sql1 = [NSString stringWithFormat:
@"INSERT INTO '%@' ('%@', '%@', '%@') VALUES ('%@', '%@', '%@')",
@"ptable", @"name", @"age", @"address", @"first name", @"23", @"pukou"];
NSString *sql2 = [NSString stringWithFormat:
@"INSERT INTO '%@' ('%@', '%@', '%@') VALUES ('%@', '%@', '%@')",
@"ptable", @"name", @"age", @"address",@"second name", @"20", @"qixia"];
if(sqlite3_exec(db, [sql1 UTF8String], NULL, NULL, &error) != SQLITE_OK){
sqlite3_close(db);
NSLog(@"can not insert sql1");
NSAssert(FALSE, @"create insert db (sql1) error");
}
if(sqlite3_exec(db, [sql2 UTF8String], NULL, NULL, &error) != SQLITE_OK){
sqlite3_close(db);
NSLog(@"can not insert sql2");
NSAssert(FALSE, @"create insert db (sql2) error");
}
//Read data and check result.
NSString *query = @"select * from ptable";
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(db, [query UTF8String], -1, &statement, nil) == SQLITE_OK){
//start to traverse data
int size = 0;
while (sqlite3_step(statement) == SQLITE_ROW) {
size++;
char *name = (char*)sqlite3_column_text(statement, 1);
int age= (int)sqlite3_column_int(statement, 2);
char *address = (char*)sqlite3_column_text(statement, 3);
NSString *nameStr = [[NSString alloc] initWithUTF8String:name];
NSString *addressStr = [[NSString alloc] initWithUTF8String:address];
NSLog(@"name: %@ age: %d address: %@",nameStr,age,addressStr);
}
NSAssert(size == 2, @"size should 2");
}else{
NSAssert(FALSE, @"query db error");
}
sqlite3_close(db);
}
-(void)testWriteToFile{
NSString *st1 = @"first";
NSString *st2 = @"second";
NSArray *array = [NSArray arrayWithObjects:st1,st2, nil];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSAssert(path != nil, @"path is nil");
NSString *filename = [path stringByAppendingPathComponent:@"test.data"];
NSAssert([array writeToFile:filename atomically:YES],@"write successfully");
NSMutableArray *savearray = [NSMutableArray arrayWithContentsOfFile:filename];
NSAssert([[savearray objectAtIndex:0] isEqualToString:st1], @"not equal to str1");
NSAssert([[savearray objectAtIndex:1] isEqualToString:st2], @"not equal to str2");
}
-(void)testUserDefault{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *name = @"test";
[defaults setObject:name forKey:@"name"];
UIImage *image = [UIImage imageNamed:@"photo"];
NSData *imageData = UIImagePNGRepresentation(image);
[defaults setObject:imageData forKey:@"image"];
//now read from data
NSString *name1= [defaults objectForKey:@"name"];
NSData *imageData1 = [defaults objectForKey:@"image"];
NSAssert([name1 isEqualToString:name], @"name1 is equal to test");
NSAssert([imageData1 isEqualToData:imageData], @"image data is not equal");
}
-(void)testKeyedArchiver{
NSString *save1 =@"test1";
NSString *save2 =@"test2";
NSArray *array = [NSArray arrayWithObjects:save1, save2, nil];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *filename = [path stringByAppendingPathComponent:@"savedatatest"];
NSLog(@"filename : %@",filename);
//save data
NSAssert([NSKeyedArchiver archiveRootObject:array toFile:filename],@"archive successfully.");
array = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
save1 = [array objectAtIndex:0];
NSAssert([save1 isEqualToString:@"test1"], @"save1 must equals to test1");
save2 = [array objectAtIndex:1];
NSAssert([save2 isEqualToString:@"test2"], @"save1 must equals to test2");
}
@end
CoreData的使用稍微复杂一些,所以将其单独放入Helper类中。Entity model包含name,age 和 address 三个属性。可在测试工程创建,并生成Entity对象。
#import "CoreDataHelper.h"
#import <CoreData/CoreData.h>
#import "Entity.h"
@interface CoreDataHelper()
@property (strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator;
-(NSManagedObjectContext *)managedObjectContext;
-(NSManagedObjectModel *)managedObjectModel;
@end
@implementation CoreDataHelper
-(void)coreDataTest{
NSError *error = nil;
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Entity" inManagedObjectContext:self.managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSArray *oldResult = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
for (Entity *bb in oldResult) {
[self.managedObjectContext deleteObject:bb];
}
NSAssert([self.managedObjectContext save:&error], @"deleting.....");
/******insert *****/
Entity *entity = [[Entity alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
entity.name = @"test";
entity.age = [NSNumber numberWithInt:20];
entity.address = @"beijing";
[self.managedObjectContext insertObject:entity];
NSAssert([self.managedObjectContext save:&error], @"inserting.....");
/*****query *****/
NSSortDescriptor *sortDescription = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:NO];
NSArray *sortDescriptions = [[NSArray alloc] initWithObjects:sortDescription, nil];
[request setSortDescriptors:sortDescriptions];
NSMutableArray *array = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
for (Entity *bb in array) {
NSLog(@"name: %@ age:%@ address:%@",bb.name,bb.age,bb.address);
}
NSAssert([array count] == 1, @"count size is equal to 1");
/***** update and check****/
NSArray *updateResult = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
Entity *first = [updateResult objectAtIndex:0];
first.name = @"one";
NSAssert([self.managedObjectContext save:&error], @"updating.....");
updateResult = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
first = [updateResult objectAtIndex:0];
NSAssert([first.name isEqualToString:@"one"], @"need to equeal one");
/**** insert and check ****/
entity = [[Entity alloc] initWithEntity:entityDescription insertIntoManagedObjectContext:nil];
entity.name = @"test2";
entity.age = [NSNumber numberWithInt:23];
entity.address = @"nanjing";
[self.managedObjectContext insertObject:entity];
NSAssert([self.managedObjectContext save:&error], @"inserting.....");
array = [[self.managedObjectContext executeFetchRequest:request error:&error] mutableCopy];
for (Entity *bb in array) {
NSLog(@"name: %@ age:%@ address:%@",bb.name,bb.age,bb.address);
}
NSAssert([array count] == 2, @"now size is equal to 2");
}
-(NSPersistentStoreCoordinator *)persistentStoreCoordinator{
if(_persistentStoreCoordinator)
return _persistentStoreCoordinator;
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSURL *storeURL = [NSURL fileURLWithPath:[documentPath stringByAppendingPathComponent:@"person.sqlite"]];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){
NSLog(@"persistent error : %@",[error userInfo]);
}
return _persistentStoreCoordinator;
}
-(NSManagedObjectContext *)managedObjectContext{
if(_managedObjectContext)
return _managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if(coordinator){
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
-(NSManagedObjectModel *)managedObjectModel{
if(_managedObjectModel)
return _managedObjectModel;
NSURL *modeUrl = [[NSBundle mainBundle] URLForResource:@"Entity" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modeUrl];
return _managedObjectModel;
}
@end