天天看點

IOS之資料持久化

9.1 資料持久化概述

9.2 iOS應用程式目錄結構

9.3 讀寫屬性清單

9.4 對象歸檔

9.5 通路SQLite

9.1 資料持久化概述

iOS中可以有四種持久化資料的方式: 屬性清單、對象歸檔、SQLite3和Core Data

9.2 iOS應用程式目錄結構

iOS應用程式運作在Mac os模拟器時候,有一下臨時目錄模拟器3.1.3為例子:

/Users/tony/Library/Application Support/iPhone Simulator/3.1.3/Applications

IOS之資料持久化

IOS應用程式采用沙盒原理設計,ios每個應用程式都有自己的3個目錄(Document,Library,tmp),互相之間不能通路。

Documents存放應用程式的資料。

Library目錄下面還有Preferences和Caches目錄,Preferences目錄存放應用程式的使用偏好,Caches目錄與Documents很相 似可以存放應用程式的資料。

tmp目錄供應用程式存儲臨時檔案。

9.3 讀寫屬性清單

讀取Documents目錄下檔案

可以獲得應用程式的Documents檔案夾。

NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];      

擷取檔案的完整路徑。

- (NSString*)filePath:(NSString*)fileName {
    NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];
    NSString* filePath = [myDocPath stringByAppendingPathComponent:fileName];
    return filePath;
}      

擷取tmp目錄

擷取應用程式的tmp目錄要比擷取Documents目錄容易的多。使用函數NSTemporaryDirectory ()可以獲得tmp目錄路徑。

NSString* tempPath = NSTemporaryDirectory();      

擷取檔案的完整路徑。

NSString* tempFile = [tempPath stringByAppendingPathComponent:@"properties.plist"];      

屬性清單檔案執行個體 :PropertesList

IOS之資料持久化

PropertesListViewController.h

#import "Student.h"

@interface ViewController : UIViewController

@property (retain, nonatomic) IBOutlet UITextField *studentNo;
@property (retain, nonatomic) IBOutlet UITextField *studentName;
@property (retain, nonatomic) IBOutlet UITextField *studentClass;
- (IBAction)save:(id)sender;
- (IBAction)load:(id)sender;
- (IBAction)endEditing:(id)sender;
- (IBAction)saveToArchiver:(id)sender;
- (IBAction)loadFromArchiver:(id)sender;
- (NSString*)filePath:(NSString*)fileName;
@end      

PropertesListViewController.m

@synthesize studentNo;
@synthesize studentName;
@synthesize studentClass;

- (NSString*)filePath:(NSString*)fileName {
    NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];
    NSString* filePath = [myDocPath stringByAppendingPathComponent:fileName];
    return filePath;
}

- (IBAction)save:(id)sender {

    NSString* fileName = [self filePath:@"properties.plist"];
    NSLog(fileName);
    NSMutableArray* data = [[NSMutableArray alloc]init];

    [data addObject:studentNo.text];
    [data addObject:studentName.text];
    [data addObject:studentClass.text];
    [data writeToFile:fileName atomically:YES];
}

- (IBAction)load:(id)sender {
    NSString* fileName = [self filePath:@"properties.plist"];
    if ([[NSFileManager defaultManager]fileExistsAtPath:fileName]) {
        NSArray* data = [[NSArray alloc]initWithContentsOfFile:fileName];
        studentNo.text = [data objectAtIndex:0];
        studentName.text = [data objectAtIndex:1];
        studentClass.text = [data objectAtIndex:2];
        [data release];
    }
    
}

- (IBAction)endEditing:(id)sender {
    [sender resignFirstResponder];
}      
IOS之資料持久化
IOS之資料持久化

9.4 對象歸檔

對象歸檔執行個體:Encoding

對象歸檔

“歸檔”是值另一種形式的序列化,對模型對象進行歸檔的技術可以輕松将複雜的對象寫入檔案,然後再從中讀取它們,隻要在類中實作的每個屬性都是基本資料類型(如int或float)或都是符合NSCoding協定的某個類的執行個體,你就可以對你的對象進行完整歸檔。

實作NSCoding協定

NSCoding協定聲明了兩個方法: -(void)encodeWithCoder:(NSCoder *)aCoder,是将對象寫入到檔案中。 

-(id)initWithCoder:(NSCoder *)aDecoder,是将檔案中資料讀入到對象中。

實作NSCopying協定

NSCopying協定聲明了一個方法: -(id)copyWithZone:(NSZone *)zone ,是将對象複制方法。 

Student.h

@interface Student : NSObject<NSCoding, NSCopying>

@property (retain, nonatomic) NSString* studentNo;
@property (retain, nonatomic) NSString* studentName;
@property (retain, nonatomic) NSString* studentClass;

@end      

Student.m

#import "Student.h"

@implementation Student

@synthesize studentNo = _studentNo;
@synthesize studentName = _studentName;
@synthesize studentClass = _studentClass;

#pragma mark NSCopying

- (id)copyWithZone:(NSZone *)zone {
    Student* copy = [[[self class]allocWithZone:zone]init];
    copy.studentNo = [_studentNo copyWithZone:zone];
    copy.studentName = [_studentName copyWithZone:zone];
    copy.studentClass = [_studentClass copyWithZone:zone];
    return copy;
}

#pragma mark  NSCoding

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_studentNo forKey:@"studentNo"];
    [aCoder encodeObject:_studentName forKey:@"studentName"];
    [aCoder encodeObject:_studentClass forKey:@"studentClass"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
    _studentNo = [aDecoder decodeObjectForKey:@"studentNo"];
    _studentName = [aDecoder decodeObjectForKey:@"studentName"];
    _studentClass = [aDecoder decodeObjectForKey:@"studentClass"];
    return self;
}
-(NSString*)description {
    return [[[NSString alloc]initWithFormat:@"no:%@ name:%@ class:%@", _studentNo, _studentName, _studentClass]autorelease];
}

- (void)dealloc {
    [_studentName release];
    [_studentClass release];
    [_studentNo release];
    [super dealloc];
}
@end      

EncodingViewController.h

詳細見上。

EncodingViewController.m

- (IBAction)saveToArchiver:(id)sender {
    NSString* fileName = [self filePath:@"student.archiver"];
    NSMutableData* data = [NSMutableData data];
    NSKeyedArchiver* archiver = [[NSKeyedArchiver alloc]initForWritingWithMutableData:data];
    Student* student = [[Student alloc]init];
    student.studentNo = studentNo.text;
    student.studentName = studentName.text;
    student.studentClass = studentClass.text;
    [archiver encodeObject:student forKey:@"myStudent"];
    [archiver finishEncoding];
    [data writeToFile:fileName atomically:YES];
    [archiver release];
    [student release];
}      

NSMutableData * theData = [NSMutableData data];用于包含編碼的資料。

NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData];建立NSKeyedArchiver執行個體,用于将對象歸檔到此theData執行個體中。

[archiver encodeObject:student forKey:@"mystudent"]; 使用“鍵-值”對編碼來對希望包含在歸檔中的對象進行歸檔。

[theData writeToFile:filename atomically:YES]; 寫入資料到歸檔檔案。

EncodingViewController.m

- (IBAction)loadFromArchiver:(id)sender {
    NSString* fileName = [self filePath:@"student.archiver"];
    NSData* data = [NSData dataWithContentsOfFile:fileName];
    if ([data length] > 0) {
        NSKeyedUnarchiver* unArchiver = [[NSKeyedUnarchiver alloc]initForReadingWithData:data];
        Student* student = [unArchiver decodeObjectForKey:@"myStudent"];
        studentNo.text = student.studentNo;
        studentName.text = student.studentName;
        studentClass.text = student.studentClass;
        [unArchiver finishDecoding];
        [unArchiver release];
    }
}      

NSData * theData =[NSData dataWithContentsOfFile:filename];從歸檔檔案中獲得NSData執行個體。

NSKeyedUnarchiver * archiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData]; 

建立一個NSKeyedUnarchiver執行個體對資料進行解碼。Student *student = [archiver decodeObjectForKey:@"mystudent"];

使用與歸檔編碼使用相同的鍵對象進行解碼。

9.5 通路SQLite

SQLite資料庫

SQLite是一個開源的嵌入式關系資料庫,它在2000年由D. Richard Hipp釋出,它的減少應用程式管理資料的開銷,SQLite可移植性好,很容易使用,很小,高效而且可靠。

SQLite嵌入到使用它的應用程式中,它們共用相同的程序空間,而不是單獨的一個程序。從外部看,它并不像一個RDBMS,但在程序内部,它卻是完整的,自包含的資料庫引擎。 嵌入式資料庫的一大好處就是在你的程式内部不需要網絡配置,也不需要管理。因為用戶端和伺服器在同一程序空間運作。SQLite 的資料庫權限隻依賴于檔案系統,沒有使用者帳戶的概念。SQLite 有資料庫級鎖定,沒有網絡伺服器。它需要的記憶體,其它開銷很小,适合用于嵌入式裝置。你需要做的僅僅是把它正确的編譯到你的程式。

SQLite資料類型

SQLite是無類型的,這意味着你可以儲存任何類型的資料到你所想要儲存的任何表的任何列中, 無

論這列聲明的資料類型是什麼,對于SQLite來說對字段不指定類型是完全有效的,如: 

Create Table ex1(a, b, c); 

SQLite允許忽略資料類型,但是仍然建議在你的Create Table語句中指定資料類型, 因為資料類型對于你和其他的程式員交流, 或者你準備換掉你的資料庫引擎。 SQLite支援常見的資料類型, 如:

IOS之資料持久化

在iOS中使用SQLite3

為了能夠在iOS中使用SQLite3需要是将libsqlite3.dylib類庫添加到Xcode工程中,在工程的Frameworks(架構) 檔案夾右鍵添加存在Frameworks

IOS之資料持久化

或者導航到 /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/ iPhoneSimulator<version>.sdk/usr/lib 目錄下面找到libsqlite3.dylib.

執行個體:StudentSQLite3

IOS之資料持久化

StudentSQLite3ViewController.h

#import "sqlite3.h"

#define DATA_FILE @"data.sqlite3"
#define TABLE_NAME @"student"
#define FIELDS_NAME_SID @"studentId"
#define FIELDS_NAME_SNAME @"studentName"
#define FIELDS_NAME_SCLASS @"studentClass"

@interface ViewController : UIViewController {
    sqlite3* db;
}
@property (retain, nonatomic) IBOutlet UITextField *studentId;
@property (retain, nonatomic) IBOutlet UITextField *studentName;
@property (retain, nonatomic) IBOutlet UITextField *studentClass;
- (IBAction)saveFromSqlite:(id)sender;
- (IBAction)loadFromSqlite:(id)sender;


-(NSString*)dataFile;
-(IBAction)textFieldDoneEditing:(id)sender;

@end      

StudentSQLite3ViewController.m

@synthesize studentId;
@synthesize studentName;
@synthesize studentClass;

-(NSString*)dataFile {
    NSArray* myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString* myDocPath = [myPaths objectAtIndex:0];
    NSString* fileName = [myDocPath stringByAppendingFormat:DATA_FILE];
    return fileName;
}      

無參數SQLite3處理過程

1、打開資料庫sqlite3_open。

2、建立資料庫表和執行SQL語句sqlite3_exec。

3、釋放資源sqlite3_close。

建立資料庫

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString* fileName = [self dataFile];
    NSLog(@"%@", fileName);
    if (sqlite3_open([fileName UTF8String], &db) != SQLITE_OK) {
        sqlite3_close(db);
        NSAssert(NO, @"OPEN SQLITE DATABASE ERROR!");
    } else {
        char* error;
        NSString* createSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@(%@ TEXT PRIMARY KEY, %@ TEXT, %@% TEXT);", 
                                  TABLE_NAME, FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS];
        if (sqlite3_exec(db, [createSQL UTF8String], NULL, NULL, &error)) {
            sqlite3_close(db);
            NSAssert1(NO, @"CREATE TABLE ERROR", error);
        } else {
            sqlite3_close(db);   
        }
    }
}      

sqlite3_open([[self dataFilePath] UTF8String], &db) != SQLITE_OK sqlite3_open打開資料庫,注意:在sqlite3中的函數都是使用C字元串[self dataFilePath] UTF8String]是将NSString字元串轉換為C字元串,&db是sqlite3指針(* db)的位址。

該函數sqlite3_open傳回SQLITE_OK打開成功。

sqlite3_exec(db, [tablesql UTF8String], NULL, NULL, &err) != SQLITE_OK

sqlite3_exec是執行任何不帶傳回值sql語句,第2個參數是要執行的sql語句,第3個參數是要回調函數,第4個參數是要回調函數的參數,第5個參數是執行出錯的字元串。

sqlite3_close(db); 是關閉資料庫。

NSAssert是斷言函數,當斷言失敗時候列印資訊。

NSAssert1是帶有一個參數的NSAssert函數,此外還有NSAssert2等函數。

有參數的SQLite3處理過程

1、打開資料庫sqlite3_open。

2、預處理SQL語句sqlite3_prepare_v2。

3、綁定參數sqlite3_bind_text。

4、執行語句sqlite3_step(statement) 。

5、釋放資源sqlite3_finalize࿨sqlite3_close。

資料儲存

- (IBAction)saveFromSqlite:(id)sender {
    NSString* fileName = [self dataFile];
    NSLog(@"%@", fileName);
    if (sqlite3_open([fileName UTF8String], &db)) {
        sqlite3_close(db);
        NSAssert(NO, @"OPEN DATABASE ERROR");
    } else {
        NSString* sqlStr = [NSString stringWithFormat:@"INSERT OR REPLACE INTO %@(%@, %@, %@) VALUES(?, ?, ?)",TABLE_NAME, FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS];
        sqlite3_stmt* statement;
        //預處理過程
        if (sqlite3_prepare(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) {
            //綁定參數開始
            sqlite3_bind_text(statement, 1, [studentId.text UTF8String], -1, NULL);
            sqlite3_bind_text(statement, 2, [studentName.text UTF8String], -1, NULL);
            sqlite3_bind_text(statement, 3, [studentClass.text UTF8String], -1, NULL);
            //執行插入
            if (sqlite3_step(statement) != SQLITE_DONE) {
                NSAssert(0, @"INSERT DATABASE ERROR!");
            }
        }
        sqlite3_finalize(statement);
        sqlite3_close(db);
    }
}      

sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, nil) == SQLITE_OK

sqlite3_prepare_v2執行sql語句,第3個參數-1代表全部sql字元串長度,第4個參數&statement是sqlite3_stmt指針(* statement)的位址,第5個參數是sql語句沒有被執行的部分語句。

sqlite3_bind_text(statement, 1, [studentId.text UTF8String], -1, NULL);

是綁定參數,第2個參數為序号(從1開始),第3個參數為字元串值,第4個參數為字元串長度。 第5個參數為一個函數指針,SQLITE3執行完操作後回調此函數,通常用于釋放字元串占用的記憶體。

sqlite3_step(statement) != SQLITE_DONE判斷是否執行完成sql語句執行。

sqlite3_finalize(statement)和sqlite3_close(db)釋放資源。

查詢資料

- (IBAction)loadFromSqlite:(id)sender {
    NSString* fileName = [self dataFile];
    NSLog(@"%@", fileName);
    if (sqlite3_open([fileName UTF8String], &db) != SQLITE_OK) {
        sqlite3_close(db);
        NSAssert(NO, @"OPEN DATABASE ERROR!");
    } else {
        NSString* sqlStr = [NSString stringWithFormat:@"SELECT %@,%@,%@ FROM %@ WHERE %@=?", 
                            FIELDS_NAME_SID, FIELDS_NAME_SNAME, FIELDS_NAME_SCLASS, TABLE_NAME, FIELDS_NAME_SID];
        sqlite3_stmt* statement;
        //預處理過程
        if (sqlite3_prepare_v2(db, [sqlStr UTF8String], -1, &statement, NULL) == SQLITE_OK) {
            //綁定參數開始
            sqlite3_bind_text(statement, 1, "1000", -1, NULL);
            //執行
            while (sqlite3_step(statement) == SQLITE_ROW) {
                char* field1 = (char*)sqlite3_column_text(statement, 0);
                NSString* field1Str = [[NSString alloc]initWithUTF8String:field1];
                studentId.text = field1Str;
                
                char* field2 = (char*)sqlite3_column_text(statement, 1);
                NSString* field2Str = [[NSString alloc]initWithUTF8String:field2];
                studentName.text = field2Str;
                
                char* field3 = (char*)sqlite3_column_text(statement, 2);
                NSString* field3Str = [[NSString alloc]initWithUTF8String:field3];
                studentClass.text = field3Str;
                
                [field1Str release];
                [field2Str release];
                [field3Str release];
            }
        }
        sqlite3_finalize(statement);
        sqlite3_close(db);
    }
}      

while (sqlite3_step(statement) == SQLITE_ROW) sqlite3_step(statement) == SQLITE_ROW單步執行并判斷sql語句執行的狀态。

char *field1 = (char *) sqlite3_column_text(statement, 0); sqlite3_column_text(statement, 0);取出字段值,第2個參數是列的順序,序号是從0開始。

NSString *field1Str = [[NSString alloc] initWithUTF8String: field1];建構NSSting字元串。

其它部分代碼

-(IBAction)textFieldDoneEditing:(id)sender {
    [sender resignFirstResponder];
}
- (void)viewDidUnload
{
    [self setStudentId:nil];
    [self setStudentName:nil];
    [self setStudentClass:nil];
    [super viewDidUnload];
}

- (void)dealloc {
    [studentId release];
    [studentName release];
    [studentClass release];
    [super dealloc];
}      

注:

1 本教程是基于關東升老師的教程

2 基于黑蘋果10.6.8和xcode4.2

3 本人初學,有什麼不對的望指教

4 教程會随着本人學習,持續更新

5 教程是本人從word筆記中拷貝出來了,是以格式請見諒