你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS應用數據存儲的常用方式我見解

iOS應用數據存儲的常用方式我見解

編輯:IOS開發綜合

iOS應用數據存儲的常用方式

  • XML屬性列表(plist)歸檔

  • Preference(偏好設置)

  • NSKeyedArchiver歸檔(NSCoding)

  • SQLite3

  • Core Data

    XML屬性列表(plist)歸檔

    屬性列表是一種XML格式的文件,拓展名為plist。

    如果對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,就可以使用writeToFile:atomically:方法直接將對象寫到屬性列表文件中。

    舉個例子:將一個NSDictionary對象歸檔到一個plist屬性列表中

    1   2   3   4   5   6   7   //將數據封裝成字典   NSMutableDictionary*dict=[NSMutableDictionarydictionary];   [dictsetObject:@"母雞"forKey:@"name"];   [dictsetObject:@"15013141314"forKey:@"phone"];   [dictsetObject:@"27"forKey:@"age"];   //將字典持久化到Documents/stu.plist文件中   [dictwriteToFile:pathatomically:YES];    

    成功寫入到Documents目錄下:

    1461840164876699.png

    運行結果

    用文本編輯器打開,文件內容為:

    1353118-b741943dd4a08d67.jpg

    文本編輯器查看

    用xcode打開屬性文件:

    1353118-79356fa27c45eed4.jpg

    xcode查看

    讀取屬性列表,恢復NSDictionary對象

    1   2   3   4   5   //讀取Documents/stu.plist的內容,實例化NSDictionary   NSDictionary*dict=[NSDictionarydictionaryWithContentsOfFile:path];   NSLog(@"name:%@",[dictobjectForKey:@"name"]);   NSLog(@"phone:%@",[dictobjectForKey:@"phone"]);   NSLog(@"age:%@",[dictobjectForKey:@"age"]);    

    1353118-b716578072700e39.jpg

    輸出結果:

    1353118-dc978e8864a6891f.jpg

    屬性列表-NSDictionary的存儲和讀取過程

    Preference(偏好設置)

    很多iOS應用都支持偏好設置,比如保存用戶名、密碼、字體大小等設置,iOS提供了一套標准的解決方案來為應用加入偏好設置功能。

    每個應用都有個NSUserDefaults實例,通過它來存取偏好設置。

    比如,保存用戶名、字體大小、是否自動登錄

    1   2   3   4   NSUserDefaults*defaults=[NSUserDefaultsstandardUserDefaults];   [defaultssetObject:@"itcast"forKey:@"username"];   [defaultssetFloat:18.0fforKey:@"text_size"];   [defaultssetBool:YESforKey:@"auto_login"];    

    1353118-55bb258a3d05246d.jpg

    存儲文件內容

    NSKeyedArchiver歸檔(NSCoding)

    如果對象是NSString、NSDictionary、NSArray、NSData、NSNumber等類型,可以直接用NSKeyedArchiver進行歸檔和恢復。

    不是所有的對象都可以直接用這種方法進行歸檔,只有遵守了NSCoding協議的對象才可以。

    NSCoding協議有2個方法:

    • encodeWithCoder:

      每次歸檔對象時,都會調用這個方法。一般在這個方法裡面指定如何歸檔對象中的每個實例變量,可以使用encodeObject:forKey:方法歸檔實例變量

      • initWithCoder:

        每次從文件中恢復(解碼)對象時,都會調用這個方法。一般在這個方法裡面指定如何解碼文件中的數據為對象的實例變量,可以使用decodeObject:forKey方法解碼實例變量

        歸檔一個NSArray對象到Documents/array.archive

        1   2   NSArray*array=[NSArrayarrayWithObjects:@”a”,@”b”,nil];   [NSKeyedArchiverarchiveRootObject:arraytoFile:path];    

        歸檔成功

        1353118-d2f19a707d722c16.jpg

        存檔文件

        恢復(解碼)NSArray對象

        1   NSArray*array=[NSKeyedUnarchiverunarchiveObjectWithFile:path];    

        1353118-391ffe7d0eca524e.jpg

        存取過程

        歸檔Person對象

        Person.h

        1   2   3   4   @interfacePerson:NSObject@property(nonatomic,copy)NSString*name;   @property(nonatomic,assign)intage;   @property(nonatomic,assign)floatheight;   @end    

        Person.m

        1   2   3   4   5   6   7   8   9   10   11   12   13   @implementationPerson   -(void)encodeWithCoder:(NSCoder*)encoder{   [encoderencodeObject:self.nameforKey:@"name"];   [encoderencodeInt:self.ageforKey:@"age"];   [encoderencodeFloat:self.heightforKey:@"height"];   }   -(id)initWithCoder:(NSCoder*)decoder{   self.name=[decoderdecodeObjectForKey:@"name"];   self.age=[decoderdecodeIntForKey:@"age"];   self.height=[decoderdecodeFloatForKey:@"height"];   returnself;   }   @end    

        歸檔(編碼)

        1   2   3   4   5   Person*person=[[[Personalloc]init]autorelease];   person.name=@"hosea";   person.age=22;   person.height=1.83f;   [NSKeyedArchiverarchiveRootObject:persontoFile:path];    

        恢復(解碼)

        1   Person*person=[NSKeyedUnarchiverunarchiveObjectWithFile:path];    

        NSKeyedArchiver-歸檔對象的注意

        1   2   3   4   5   6   7   -如果父類也遵守了NSCoding協議,請注意:   -應該在encodeWithCoder:方法中加上一句   [superencodeWithCode:encode];   確保繼承的實例變量也能被編碼,即也能被歸檔   -應該在initWithCoder:方法中加上一句   self=[superinitWithCoder:decoder];   確保繼承的實例變量也能被解碼,即也能被恢復  
         
         

        歸檔NSData

        使用archiveRootObject:toFile:方法可以將一個對象直接寫入到一個文件中,但有時候可能想將多個對象寫入到同一個文件中,那麼就要使用NSData來進行歸檔對象。

        NSData可以為一些數據提供臨時存儲空間,以便隨後寫入文件,或者存放從磁盤讀取的文件內容。可以使用[NSMutableData data]創建可變數據空間。

        1353118-4ed13bedff51f5d4.jpg

        原理

        舉個例子:NSData-歸檔2個Person對象到同一文件中

        歸檔(編碼)

        1   2   3   4   5   6   7   8   9   10   11   //新建一塊可變數據區   NSMutableData*data=[NSMutableDatadata];   //將數據區連接到一個NSKeyedArchiver對象   NSKeyedArchiver*archiver=[[NSKeyedArchiveralloc]initForWritingWithMutableData:data];   //開始存檔對象,存檔的數據都會存儲到NSMutableData中   [archiverencodeObject:person1forKey:@"person1"];   [archiverencodeObject:person2forKey:@"person2"];   //存檔完畢(一定要調用這個方法,調用了這個方法,archiver才會將encode的數據存儲到NSMutableData中)   [archiverfinishEncoding];   //將存檔的數據寫入文件   [datawriteToFile:pathatomically:YES];    

        恢復(解碼)

        1   2   3   4   5   6   7   8   //從文件中讀取數據   NSData*data=[NSDatadataWithContentsOfFile:path];   //根據數據,解析成一個NSKeyedUnarchiver對象   NSKeyedUnarchiver*unarchiver=[[NSKeyedUnarchiveralloc]initForReadingWithData:data];   Person*person1=[unarchiverdecodeObjectForKey:@"person1"];   Person*person2=[unarchiverdecodeObjectForKey:@"person2"];   //恢復完畢(這個方法調用之後,unarchiver不能再decode對象,而且會通知unarchiver的代理調用unarchiverWillFinish:和unarchiverDidFinish:方法)   [unarchiverfinishDecoding];    

        PS:也可將多個對象放入到一個數組中。

        • 將數組進行歸檔,在數組對象執行archiveRootObject:toFile時,數組中每個對象會自動調用encodeWithCoder:方法進行歸檔;

        • 相反數組文件進行解檔時,在數組對象執行unarchiveObjectWithFile:時,數組中每個對象會自動調用initWithCoder:方法進行解檔。

          利用歸檔實現深復制

          比如對一個Person對象進行深復制

          1   2   3   4   5   6   7   //臨時存儲person1的數據   NSData*data=[NSKeyedArchiverarchivedDataWithRootObject:person1];   //解析data,生成一個新的Person對象   Person*person2=[NSKeyedUnarchiverunarchiveObjectWithData:data];   //分別打印內存地址   NSLog(@"person1:0x%x",person1);//person1:0x7177a60   NSLog(@"person2:0x%x",person2);//person2:0x7177cf0    

          1353118-ffc98002136a4b44.jpg

          深復制原理


           
           
           
           
           
           
           
           
           
           

          SQLite3

          SQLite3簡介

          SQLite3是一款開源的嵌入式關系型數據庫,可移植性好、易使用、內存開銷小。

          SQLite3是無類型的,意味著你可以保存任何類型的數據到任意表的任意字段中。比如下列的創表語句是合法的:

          1   createtablet_person(name,age);    

          為了保證可讀性,建議還是把字段類型加上:

          1   createtablet_person(nametext,ageinteger);    

          SQLite3常用的5種數據類型:text、integer、float、boolean、blob

          在iOS中使用SQLite3,首先要添加庫文件libsqlite3.dylib和導入主頭文件

          1353118-d7cd70cf7fca9851.jpg

          導入庫

          創建或打開數據庫

          1   2   3   //path為:~/Documents/person.db   sqlite3*db;   intresult=sqlite3_open([pathUTF8String],&db);    

          代碼解析:

          • sqlite3_open()將根據文件路徑打開數據庫,如果不存在,則會創建一個新的數據庫。如果result等於常量SQLITE_OK,則表示成功打開數據庫

          • sqlite3 *db:一個打開的數據庫實例

          • 數據庫文件的路徑必須以C字符串(而非NSString)傳入

            關閉數據庫:sqlite3_close(db);

            執行創表語句

            1   2   3   char*errorMsg;//用來存儲錯誤信息   char*sql="createtableifnotexistst_person(idintegerprimarykeyautoincrement,nametext,ageinteger);";   intresult=sqlite3_exec(db,sql,NULL,NULL,&errorMsg);    

            代碼解析:

            • sqlite3_exec()可以執行任何SQL語句,比如創表、更新、插入和刪除操作。但是一般不用它執行查詢語句,因為它不會返回查詢到的數據

            • sqlite3_exec()還可以執行的語句:

              • 開啟事務:begin transaction;

              • 回滾事務:rollback;

              • 提交事務:commit;

                帶占位符插入數據

                1   2   3   4   5   6   7   8   9   10   char*sql="insertintot_person(name,age)values(?,?);";   sqlite3_stmt*stmt;   if(sqlite3_prepare_v2(db,sql,-1,&stmt,NULL)==SQLITE_OK){   sqlite3_bind_text(stmt,1,"母雞",-1,NULL);   sqlite3_bind_int(stmt,2,27);   }   if(sqlite3_step(stmt)!=SQLITE_DONE){   NSLog(@"插入數據錯誤");   }   sqlite3_finalize(stmt);    

                代碼解析:

                • sqlite3_prepare_v2()返回值等於SQLITE_OK,說明SQL語句已經准備成功,沒有語法問題

                • sqlite3_bind_text():大部分綁定函數都只有3個參數

                  • 第1個參數是sqlite3_stmt *類型

                  • 第2個參數指占位符的位置,第一個占位符的位置是1,不是0

                  • 第3個參數指占位符要綁定的值

                  • 第4個參數指在第3個參數中所傳遞數據的長度,對於C字符串,可以傳遞-1代替字符串的長度

                  • 第5個參數是一個可選的函數回調,一般用於在語句執行後完成內存清理工作

                    • sqlite_step():執行SQL語句,返回SQLITE_DONE代表成功執行完畢

                    • sqlite_finalize():銷毀sqlite3_stmt *對象

                      查詢數據

                      1   2   3   4   5   6   7   8   9   10   11   12   char*sql="selectid,name,agefromt_person;";   sqlite3_stmt*stmt;   if(sqlite3_prepare_v2(db,sql,-1,&stmt,NULL)==SQLITE_OK){   while(sqlite3_step(stmt)==SQLITE_ROW){   int_id=sqlite3_column_int(stmt,0);   char*_name=(char*)sqlite3_column_text(stmt,1);   NSString*name=[NSStringstringWithUTF8String:_name];   int_age=sqlite3_column_int(stmt,2);   NSLog(@"id=%i,name=%@,age=%i",_id,name,_age);   }   }   sqlite3_finalize(stmt);    

                      代碼解析

                      • sqlite3_step()返回SQLITE_ROW代表遍歷到一條新記錄

                      • sqlite3_column_*()用於獲取每個字段對應的值,第2個參數是字段的索引,從0開始

                         

                        Core Data

                        Core Data簡單介紹

                        • Core Data框架提供了對象-關系映射(ORM)的功能,即能夠將OC對象轉化成數據,保存在SQLite3數據庫文件中,也能夠將保存在數據庫中的數據還原成OC對象。在此數據操作期間,不需要編寫任何SQL語句。使用此功能,要添加CoreData.framework和導入主頭文件CoreData/CoreData.h。

                          1353118-801e5c3548ba5238.jpg

                          對象-關系映射

                          • 在Core Data,需要進行映射的對象稱為實體(entity),而且需要使用Core Data的模型文件來描述應用的所有實體和實體屬性

                            這裡以Person和Card(身份證)2個實體為例子,先看看實體屬性和之間的關聯關系

                            1353118-bcb9d93c189ca107.jpg

                            實體屬性和之間的關聯關系

                            • Person中有個Card屬性,Card中有個Person屬性。

                            • 屬於一對一雙向關聯。

                              模型文件

                              創建文件

                              1353118-847db2b702ae2142.jpg

                              添加實體

                              1353118-8e522e29273d4c03.png

                              添加Person實體的基本屬性

                              1353118-3c28a1adca613a50.jpg

                              添加Card實體的基本屬性

                              1353118-e5bc2b8fa5ccce37.jpg

                              在Person中添加card屬性

                              1353118-816276353724915a.jpg

                              在Card中添加person屬性

                              1353118-8bb511e4742b94f7.jpg

                              NSManagedObject

                              • 通過Core Data從數據庫取出的對象,默認情況下都是NSManagedObject對象

                              • NSManagedObject的工作模式有點類似於NSDictionary對象,通過鍵-值對來存取所有的實體屬性

                                • setValue:forKey: 存儲屬性值(屬性名為key)

                                • valueForKey: 獲取屬性值(屬性名為key)

                                  1353118-21be3c3c51f728ac.jpg

                                  Core Data主要對象

                                  1353118-e79a0387a70eb1cb.jpg

                                  搭建Core Data上下文環境

                                  從應用程序包中加載模型文件

                                  1   NSManagedObjectModel*model=[NSManagedObjectModelmergedModelFromBundles:nil];    

                                  傳入模型,初始化NSPersistentStoreCoordinator

                                  1   NSPersistentStoreCoordinator*psc=[[[NSPersistentStoreCoordinatoralloc]initWithManagedObjectModel:model]autorelease];    

                                  構建SQLite文件路徑

                                  1   2   NSString*docs=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES)lastObject];   NSURL*url=[NSURLfileURLWithPath:[docsstringByAppendingPathComponent:@"person.data"]];    

                                  添加持久化存儲庫,這裡使用SQLite作為存儲庫

                                  1   2   3   4   5   NSError*error=nil;   NSPersistentStore*store=[pscaddPersistentStoreWithType:NSSQLiteStoreTypeconfiguration:nilURL:urloptions:nilerror:&error];   if(store==nil){//直接拋異常   [NSExceptionraise:@"添加數據庫錯誤"format:@"%@",[errorlocalizedDescription]];   }    

                                  初始化上下文,設置persistentStoreCoordinator屬性

                                  1   2   3   NSManagedObjectContext*context=[[NSManagedObjectContextalloc]init];   context.persistentStoreCoordinator=psc;   //用完之後,還是要[contextrelease];    

                                  添加數據

                                  傳入上下文,創建一個Person實體對象

                                  1   NSManagedObject*person=[NSEntityDescriptioninsertNewObjectForEntityForName:@"Person"inManagedObjectContext:context];    

                                  設置簡單屬性

                                  1   2   [personsetValue:@"hosea"forKey:@"name"];   [personsetValue:[NSNumbernumberWithInt:22]forKey:@"age"];    

                                  傳入上下文,創建一個Card實體對象

                                  1   2   NSManagedObject*card=[NSEntityDescriptioninsertNewObjectForEntityForName:@"Card"inManagedObjectContext:context];   [cardsetValue:@"4414241933432"forKey:@"no"];    

                                  設置Person和Card之間的關聯關系

                                  1   [personsetValue:cardforKey:@"card"];    

                                  利用上下文對象,將數據同步到持久化存儲庫

                                  1   2   3   4   5   6   NSError*error=nil;   BOOLsuccess=[contextsave:&error];   if(!success){   [NSExceptionraise:@"訪問數據庫錯誤"format:@"%@",[errorlocalizedDescription]];   }   //如果是想做更新操作:只要在更改了實體對象的屬性後調用[contextsave:&error],就能將更改的數據同步到數據庫    

                                  查詢數據

                                  初始化一個查詢請求

                                  1   NSFetchRequest*request=[[[NSFetchRequestalloc]init]autorelease];    

                                  設置要查詢的實體

                                  1   NSEntityDescription*desc=[NSEntityDescriptionentityForName:@"Person"inManagedObjectContext:context];    

                                  設置排序(按照age降序)

                                  1   2   NSSortDescriptor*sort=[NSSortDescriptorsortDescriptorWithKey:@"age"ascending:NO];   request.sortDescriptors=[NSArrayarrayWithObject:sort];    

                                  設置條件過濾(name like '%hosea-1%')

                                  1   2   NSPredicate*predicate=[NSPredicatepredicateWithFormat:@"namelike%@",@"*hosea-1*"];   request.predicate=predicate;    

                                  執行請求

                                  1   2   3   4   5   NSError*error=nil;   NSArray*objs=[contextexecuteFetchRequest:requesterror:&error];   if(error){   [NSExceptionraise:@"查詢錯誤"format:@"%@",[errorlocalizedDescription]];   }    

                                  遍歷數據

                                  1   2   3   for(NSManagedObject*objinobjs){   NSLog(@"name=%@",[objvalueForKey:@"name"]);   }    

                                  刪除數據

                                  傳入需要刪除的實體對象

                                  1   [contextdeleteObject:managedObject];    

                                  將結果同步到數據庫

                                  1   2   3   4   5   NSError*error=nil;   [contextsave:&error];   if(error){   [NSExceptionraise:@"刪除錯誤"format:@"%@",[errorlocalizedDescription]];   }    

                                  打開Core Data的SQL日志輸出開關

                                  1353118-5d4b45107f5a7280.jpg

                                  Core Data的延遲加載

                                  • Core Data不會根據實體中的關聯關系立即獲取相應的關聯對象

                                  • 比如通過Core Data取出Person實體時,並不會立即查詢相關聯的Card實體;當應用真的需要使用Card時,才會查詢數據庫,加載Card實體的信息

                                    創建NSManagedObject的子類

                                    默認情況下,利用Core Data取出的實體都是NSManagedObject類型的,能夠利用鍵-值對來存取數據

                                    但是一般情況下,實體在存取數據的基礎上,有時還需要添加一些業務方法來完成一些其他任務,那麼就必須創建NSManagedObject的子類

                                    1353118-0ae824488f87d4c7.jpg

                                    選擇模型文件

                                    1353118-ad48dc961b0dc63b.jpg

                                    選擇需要創建子類的實體

                                    1353118-7d62d41d5e888d25.jpg

                                    1353118-c2e164c1d17cd6ee.jpg

                                    那麼生成一個Person實體對象就應該這樣寫

                                    1   2   3   4   5   6   Person*person=[NSEntityDescriptioninsertNewObjectForEntityForName:@"Person"inManagedObjectContext:context];   person.name=@"hosea";   person.age=[NSNumbernumberWithInt:22];   Card*card=[NSEntityDescriptioninsertNewObjectForEntityForName:@”Card"inManagedObjectContext:context];   card.no=@”4414245465656";   person.card=card;    
                           
                           
         
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved