iOS Data Presistence

什么是Data Presistence呢?说白了就是怎么把数据永久性存在设备里以备后用。

关于Data Presistence大概有6中方法

1. Plaintext, CSV (Character Separated Value)
2. XML
3. Property List (plist)
4. User Preferences (NSUserDefaults)
5. SQLite (libsqlite3.dylib framework required)
6. Core Data (CoreData.framework required)

1. Plaintext – 明文; CSV

其实这个真没有什么好讲的,无非就是string。
而如何将NSString写入文件,我在之前的文章里讲过了,不在啰嗦。

2. XML

这个并不是很常用了吧,就连网络传输也逐渐被JSON替代了。pass

3. Property List (plist) – 属性列表

这个还是有必要说一下的。
其实plist就是一个archived array or dictionary,你完全可以把plist想象成数组或者字典。
但是这个collection里面只可以是NSString, NSNumber, NSData, NSDate, NSArray and NSDictionary。

// 所以第一步自然就是创建一个数组或者字典,这里以array为例子。
NSMutableArray *arryProducts  = [[NSMutableArray alloc] init];

// 你可以向这个array中添加dictionary
[arryProducts addObject:
        [[NSDictionary alloc] initWithObjectsAndKeys: (id), @"name", (id), @"price", nil]];
// 你可以向这个array中添加NSString
[arryProducts addObject:@"This is a string."];
// 各种添加...

// 接下来你选择一个地方将这个array写成plist
/*******************重要*******************/
// 你不应该把这个.plist文件写在工程文件中,而是写在app里面

// 获得documents路径,准备将plist写入这里
NSString *documentsDirectory = 
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];

// 获得完整.plist路径    
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"PLIST_products.plist"];

// 你应首先查看指定地方是不是已经存在此文件,如果有则赋值给当前array
if ([[NSFileManager defaultManager] fileExistsAtPath:plistPath]){
// 非ARC模式中记得 [arryProducts release];
    arryProducts = [NSMutableArray arrayWithContentsOfFile: plistPath];
}

// 使用array的方法写入.plist文件
[arryProducts writeToFile:plistPath atomically:YES];

/*******************修改*******************/
// 修改数据模型arryProducts后再次写入

/*******************删除*******************/
// 修改数据模型arryProducts后再次写入

4. User Preferences (NSUserDefaults)

这个NSUserDefaults实际上跟plist差不多,但是你不应该向里面存入除了用户喜好设置之外的任何东西。
下面这个例子中,将向里面存入用户的用户名和密码。但这些东西只为了演示,你应该将敏感信息存入keyChain中。

// 获取当前用户的NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

//你可以像这样直接设置key value
[defaults setObject:(id) forKey:@"Username"];
[defaults setObject:(id) forKey:@"Password"];

// 同步保存defaults
[defaults synchronize];

// 或者将NSUserDefaults赋值给一个dictionary并以此为数据模型显示在屏幕中(比如tableView)
NSMutableDictionary *mdicSettings = 
      [[NSMutableDictionary alloc] initWithDictionary: [defaults dictionaryRepresentation]];

/*******************如何获取信息*******************/
NSString *strUsername = [defaults objectForKey:@"Username"];
NSString *strPassword = [defaults objectForKey:@"Password"];

5. SQLite (libsqlite3.dylib framework required)

这个是稍微重量级一些的数据存储。在有多个collection或者有复杂的查询操作是使用。
它是一个C风格的库,你可在这里找到更多有用的方法和信息。

首先你应该在工程中添加libsqlite3.dylib framework。
并在要使用的类头中引用

#import <sqlite3.h>

接下来是具体使用方法。
首先你需要创建一个数据库文件


// - Check to see if SQLite db file exists, else create
	
sqlite3 *database = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
	
NSString *sqlitePath = [documentsDirectory stringByAppendingPathComponent:@"SQLITE_products.sqlite"];
if (![fileManager fileExistsAtPath:sqlitePath])
{
  if(![fileManager createFileAtPath:sqlitePath contents:nil attributes:nil])
  {
    NSLog(@"[ERROR] SQLITE Database failed to initialize! File could not be created in application.");
  } 
  else 
  {
    if(sqlite3_open([sqlitePath UTF8String], &database) == SQLITE_OK) 
    {
      sqlite3_exec(database, "CREATE TABLE IF NOT EXISTS products (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, price REAL)", NULL, NULL, NULL);
      sqlite3_close(database);
      database = nil;
    } 
    else 
    {
      NSLog(@"[ERROR] SQLITE Could not seed tables!");
    }

  }
}

// 开启到数据库的连接
// 你必须在使用完之后关闭它!!!!
sqlite3_open([sqlitePath UTF8String], &database);

你必须在使用完数据库之后关闭它,比如在dealloc中关闭就是一个好办法

- (void)dealloc 
{

// ...

sqlite3_close(database);
}

如何获取信息

// 定义一个SQLite database
sqlite3 *database;

// 在这里定义了一个array作为数据模型
NSMutableArray *arryProdects;

/*******************如何获取信息*******************/
// 声明了一个statement用来承装sql
sqlite3_stmt *statement;

// call prepare using statement as a parameter
// 下面有这个函数的说明。需要注意的是这个是C风格的函数,不可以传入NSString
// 这里select了3个column分别是id,name,price,你需要记住次序,后面会用到。
if(sqlite3_prepare_v2(database, "SELECT id, name, price FROM products", -1, &statement, nil) != SQLITE_OK)
{
// 如果抛出了一个error则返回,并将错误显示在控制台里。
// 注意sqlite3_errmsg的用法, 它返回最近一次发生的错误。
   NSLog(@"[ERROR] SQLITE: Failed to prepare statement! Error: '%s'", sqlite3_errmsg(database));
   return;
}

// 跟其他编程语言读取数据库时一样,需要一个loop来读取所有符合查询条件的行
// 每一个step都会有个结束标示,SQLITE_ROW说明还有新的数据已经ready
while(sqlite3_step(statement) == SQLITE_ROW) 
{
// 这里遇到之前说的select的序列了,0号是个int型数据"id"
NSNumber *recordID = [NSNumber numberWithInt: sqlite3_column_int(statement, 0)];

// 这里遇到之前说的select的序列了,1号是个string型数据"name"。
// 注意使用text代表string,并且都返回UTF8,C风格的字符串7
NSString *name = [[NSString alloc] initWithUTF8String: (char *)sqlite3_column_text(statement, 1)];
NSString *price = [[NSString alloc] initWithUTF8String: (char *)sqlite3_column_text(statement, 2)];
		
// 将每条记录以字典的形式存储,并加入到数据模型arryProjects中
NSDictionary *product = [[NSDictionary alloc] initWithObjectsAndKeys: recordID, @"id", 
            name, @"name", [NSNumber numberWithDouble:[price doubleValue]],@"price", nil];
[arryProducts addObject: product];

// call release in non-ARC mode for strings and product
}
	
// 在结束时,你必须调用这个方法或者sqlite3_reset方法
sqlite3_finalize(statement);
// 有关sqlite3_prepare_v2函数
SQLITE_API int sqlite3_prepare_v2(
  sqlite3 *db,            /* Database handle */
  const char *zSql,       /* SQL statement, UTF-8 encoded */
  int nByte,              /* Maximum length of zSql in bytes. */
  sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
  const char **pzTail     /* OUT: Pointer to unused portion of zSql */
);
/*******************如何创建一条新记录*******************/
if (!database) 
{
  NSLog(@"[ERROR] SQLITE: Database not available");
  return;
}
	
sqlite3_stmt *statement;
	
// 准备设置value的时候可以暂时用"?"代替,并在后面将"?"与特定的值绑定起来。
if(sqlite3_prepare_v2(database, "INSERT INTO products (name, price) VALUES(?,?)", -1, &statement, nil) != SQLITE_OK)
{
  NSLog(@"[ERROR] SQLITE: Failed to prepare statement!");
  return;
}
	
// 使用bind方法将值与上面的"?"绑定起来。 注意参数必须是C风格,所以使用了UIF8String方法
sqlite3_bind_text(statement, 1, [arg_name UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(statement, 2, [arg_price doubleValue]);
	
// 因为只有一步,只要不报错就说明正常
if (sqlite3_step(statement) == SQLITE_ERROR)
{
  NSLog(@"[ERROR] SQLITE: Failed to insert into database! Error: '%s'", sqlite3_errmsg(database));
  return;
}
	
sqlite3_finalize(statement);
	
// sqlite3_last_insert_rowid 方法会返回你最后插入行的一个64位有符号独特的table id
// 多线程的使用会使这个返回值变得不确定
// 但是如果你的table中有一个int型主键,这个键值将会被返回
/*
** CAPI3REF: Last Insert Rowid
**
** ^Each entry in an SQLite table has a unique 64-bit signed
** integer key called the [ROWID | "rowid"]. ^The rowid is always available
** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
** names are not also used by explicitly declared columns. ^If
** the table has a column of type [INTEGER PRIMARY KEY] then that column
** is another alias for the rowed.
*/
NSDictionary *product = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt: sqlite3_last_insert_rowid(database)], @"id", arg_name, @"name", arg_price, @"price", nil];
/*******************如何修改信息*******************/
sqlite3_stmt *statement;
	
if(sqlite3_prepare_v2(database, "UPDATE products SET name = ?, price = ? WHERE id = ?", -1, &statement, nil) != SQLITE_OK)
{
  NSLog(@"[ERROR] SQLITE: Failed to prepare statement!");
  return;
}
	
sqlite3_bind_text(statement, 1, [arg_name UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_double(statement, 2, [arg_price doubleValue]);
sqlite3_bind_int(statement, 3, [[product objectForKey:@"id"] intValue]);
	
if (sqlite3_step(statement) == SQLITE_ERROR) 
{
  NSLog(@"[ERROR] SQLITE: Failed to update record in the database! Error: '%s',
                                                        sqlite3_errmsg(database));
  return;
}
	
sqlite3_finalize(statement);
/*******************如何删除信息*******************/
if (!database) 
{
  NSLog(@"[ERROR] SQLITE: Database not available in controller!");
  return;
}
	
NSString *deleteSql = [NSString stringWithFormat:@"DELETE FROM products WHERE id = %i", 
                                                                      [arg_id intValue]];
	
if (sqlite3_exec(database, [deleteSql UTF8String], NULL, NULL, NULL) == SQLITE_ABORT) 
{
  NSLog(@"[ERROR] SQLITE: Failed to delete record from the database! Error: '%s', 
                                                              sqlite3_errmsg(database));
  return;
}
	
NSLog(@"[SUCCESS] SQLITE: Record deleted from the database! at: %i", 
                                                             sqlite3_changes(database));

6. Core Data (CoreData.framework required)

发表评论

电子邮件地址不会被公开。 必填项已用*标注