不論是Mac OS X 仍是iOS的文件系統都是創建在UNIX文件系統基礎之上的。 html
在iOS中,一個App的讀寫權限只侷限於本身的沙盒目錄中。 git
沙盒模型到底有哪些好處呢?
安全:別的App沒法修改你的程序或數據
保護隱私:別的App沒法讀取你的程序和數據
方便刪除:由於一個App全部產生的內容都在本身的沙盒中,因此刪除App只須要將沙盒刪除就能夠完全刪除程序了 github
iOS App沙盒中的目錄 sql
若是咱們想在程序中獲取上面某個目錄的路徑,應該如何實現呢? 下面就講講路徑的獲取, 經過NSPathUtilities.h中的NSSearchPathForDirectoriesInDomains函數,咱們即可以獲取咱們想要的路徑。 此函數具體聲明以下: 數據庫
NSArray *NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory directory, NSSearchPathDomainMask domainMask, BOOL expandTilde);
directory 目錄類型 好比Documents目錄 就是NSDocumentDirectory
domainMask 在iOS的程序中這個取NSUserDomainMask
expandTilde YES,表示將~展開成完整路徑 編程
注意函數返回的類型爲數組,在iOS中通常這個數組中只包含一個元素,因此直接取lastObject便可。 數組
NSFileManager提供一個類方法得到一個單例。 安全
/* Returns the default singleton instance.*/ + (NSFileManager *)defaultManager;
下面羅列了NSFileManager的經常使用方法 多線程
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(NSDictionary *)attributes error:(NSError **)error;
createIntermediates這個參數通常爲YES,表示若是目錄路徑中間的某個目錄不存在則建立之,若是是NO的話,則要保證所建立目錄的父目錄都必須已經存在 app
- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error;
若是目錄爲空,則返回空數組
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error; - (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error; - (BOOL)linkItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error; - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;
更多的能夠查看文檔 NSFileManager Class Reference。
在實際項目中,咱們通常會寫一個工具類來負責項目中全部的路徑操做。
咱們常常聽到「序列化」,「反序列化」這樣的字眼,其實「序列化」的意思就是將對象轉換成字節流以便保存或傳輸,「反序列化」即是一個相反的過程,從字節流轉到對象。
在這節中涉及到一種文件類型plist,plist就是Property List 的縮寫,即所謂的屬性列表,屬性列表有兩種數據格式,一種是XML的,方便閱讀和編輯;另外一種是二進制的,節省存儲空間,以及提升效率。
在Objective-C中這個對象和字節流的互轉分紅兩類:
不過本質上講上述兩種都是對象圖(Object Graph)和字節流之間的轉換. Apple關於序列化和歸檔的編程指南: Archives and Serializations Programming Guide 。
若是咱們須要將自定義的一個對象保存到文件,應該如何作呢?
這裏引入兩個東西:一個是NSCoding協議 ;另外一個是NSKeyedArchiver,NSKeyedArchiver其實繼承於NSCoder,能夠以鍵值對的方式將對象的屬性進行序列化和反序列化。
具體的過程能夠這樣描述 經過NSKeyedArchiver 能夠將實現了NSCoding協議的對象 和 字節流 相互轉換 。
像一些框架中的數據類型如NSDictionary,NSArray,NSString... 都已經實現了NSCoding協議,因此能夠直接對他們進行歸檔操做。
這裏來一個比較完整的例子,一個Address類,一個User類,User類下有個Address類型的屬性。
Address類
@interface Address : NSObject<NSCoding>{ NSString *country; NSString *city; } @property(nonatomic,copy) NSString *country; @property(nonatomic,copy) NSString *city; @end ////////////////////////////////////////////////////// #import "Address.h" @implementation Address @synthesize country; @synthesize city; - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:country forKey:@"country"]; [aCoder encodeObject:city forKey:@"city"]; } - (id)initWithCoder:(NSCoder *)aDecoder{ if (self = [super init]) { [self setCountry:[aDecoder decodeObjectForKey:@"country"]]; [self setCity:[aDecoder decodeObjectForKey:@"city"]]; } return self; } @end
User類
#import <Foundation/Foundation.h> #import "Address.h" @interface User : NSObject<NSCoding>{ NSString *_name; NSString *_password; Address *_address; } @property(nonatomic,copy) NSString *name; @property(nonatomic,copy) NSString *password; @property(nonatomic,retain) Address *address; @end ///////////////////////////////////////////////////////// #import "User.h" @implementation User @synthesize name = _name; @synthesize password = _password; @synthesize address = _address; - (void)encodeWithCoder:(NSCoder *)aCoder{ [aCoder encodeObject:_name forKey:@"name"]; [aCoder encodeObject:_password forKey:@"password"]; [aCoder encodeObject:_address forKey:@"address"]; } - (id)initWithCoder:(NSCoder *)aDecoder{ if (self = [super init]) { [self setName:[aDecoder decodeObjectForKey:@"name"]]; [self setPassword:[aDecoder decodeObjectForKey:@"password"]]; [self setAddress:[aDecoder decodeObjectForKey:@"address"]]; } return self; } @end
使用示例
Address *myAddress = [[[Address alloc] init] autorelease]; myAddress.country = @"中國"; myAddress.city = @"杭州"; User *user = [[[User alloc] init] autorelease]; user.name = @"盧克"; user.password = @"lukejin"; user.address = myAddress; [NSKeyedArchiver archiveRootObject:user toFile:@"/Users/Luke/Desktop/user"]; id object = [NSKeyedUnarchiver unarchiveObjectWithFile:@"/Users/Luke/Desktop/user"]; NSLog(@"Object Class : %@",[object class]);
經過查看文件內容能夠發現,保存的是plist的二進制數據格式。 轉成XML能夠看到以下內容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/ PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>$archiver</key> <string>NSKeyedArchiver</string> <key>$objects</key> <array> <string>$null</string> <dict> <key>$class</key> <dict> <key>CF$UID</key> <integer>8</integer> </dict> <key>address</key> <dict> <key>CF$UID</key> <integer>4</integer> </dict> <key>name</key> <dict> <key>CF$UID</key> <integer>2</integer> </dict> <key>password</key> <dict> <key>CF$UID</key> <integer>3</integer> </dict> </dict> <string>盧克</string> <string>lukejin</string> <dict> <key>$class</key> <dict> <key>CF$UID</key> <integer>7</integer> </dict> <key>city</key> <dict> <key>CF$UID</key> <integer>6</integer> </dict> <key>country</key> <dict> <key>CF$UID</key> <integer>5</integer> </dict> </dict> <string>中國</string> <string>杭州</string> <dict> <key>$classes</key> <array> <string>Address</string> <string>NSObject</string> </array> <key>$classname</key> <string>Address</string> </dict> <dict> <key>$classes</key> <array> <string>User</string> <string>NSObject</string> </array> <key>$classname</key> <string>User</string> </dict> </array> <key>$top</key> <dict> <key>root</key> <dict> <key>CF$UID</key> <integer>1</integer> </dict> </dict> <key>$version</key> <integer>100000</integer> </dict> </plist>
在實際的項目中,咱們通常是將NSDictionary或NSArray的對象保存到文件或者從文件讀取成對象。 固然這種只是適用於數據量不是很大的應用場景。 NSDictionary和NSArray 都有一個寫入文件的方法
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;
NSDictionary和NSArray會直接寫成plist文件。
序列化能夠經過兩種途徑來進行
寫文件
NSMutableDictionary *dataDictionary = [[[NSMutableDictionary alloc] init] autorelease]; [dataDictionary setValue:[NSNumber numberWithInt:222] forKey:@"intNumber"]; [dataDictionary setValue:[NSArray arrayWithObjects:@"1",@"2", nil] forKey:@"testArray"]; [dataDictionary writeToFile:@"/Users/Luke/Desktop/test.plist" atomically:YES];
寫完的文件內容以下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>intNumber</key> <integer>222</integer> <key>testArray</key> <array> <string>1</string> <string>2</string> </array> </dict> </plist>
從文件讀取
NSDictionary *dictionaryFromFile = [NSDictionary dictionaryWithContentsOfFile:@"/Users/Luke/Desktop/test.plist"];
經過NSPropertyListSerialization類能夠將數據對象直接轉成NSData或者直接寫到文件或者流中去.
NSMutableDictionary *dataDictionary = [[[NSMutableDictionary alloc] init] autorelease]; [dataDictionary setValue:[NSNumber numberWithInt:222] forKey:@"intNumber"]; [dataDictionary setValue:[NSArray arrayWithObjects:@"1",@"2", nil] forKey:@"testArray"]; NSString *error; NSData *xmlData = [NSPropertyListSerialization dataFromPropertyList:dataDictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&error]; if(xmlData) { NSLog(@"No error creating XML data."); [xmlData writeToFile:@"/Users/Luke/Desktop/test2.plist" atomically:YES]; } else { if (error) { NSLog(@"error:%@", error); [error release]; } }
讀取
NSDictionary *dictionaryFromFile = (NSDictionary *)[NSPropertyListSerialization propertyListWithData:[NSData dataWithContentsOfFile:@"/Users/Luke/Desktop/test2.plist"] options:0 format:NULL error:&error];
User Defaults 顧名思義就是一個用戶爲系統以及程序設置的默認值。每一個用戶都有本身的一套數據,用戶和用戶之間無法共享的。
咱們都知道每個程序都會保存一些設置數據,好比記住上次窗口的位置和大小,記住是否彈出某些提示信息等。蘋果提供了一個統一的解決方案,就是每個app都有一個plist文件專門用以保存偏好設置數據。 plist文件名默認是程序Bundle identifier,擴展名爲plist.
除了程序本身的設置外,系統還有一些全局的或者其它的一些設置,也屬於User Defaults的範疇,User Defaults的持久化數據都保存在 ~/Library/Preferences 目錄中.
這裏有一點簡要的說一下,User Defaults 中存放的key value分放在多個Domain中,取的時候按必定的次序取查找,次序以下:
Mac系統還爲user defaults提供了很好的命令行工具,defaults 你能夠經過下面的方式查看具體使用方式
man defaults
能夠經過defaults domains查看當前用戶的全部的domain,經過 defaults read NSGlobalDomain 讀取 The Global Domain 中的全部值。
NSUserDefaults 類來讀寫Preferences設置,而無需考慮文件位置等細節問題。
NSUserDefaults 用起來和 NSDictionary 很類似,多了一個Domain的概念在裏面。NSUserDefaults 同樣提供了一個獲取單例的方法.
+ (NSUserDefaults *)standardUserDefaults
NSUserDefaults提供了一系列的接口來根據key獲取對應的value,搜索的次序按照上面說起到的次序在各個Domain中進行查找。還提供了一系列的 Setting Default Values的方法,這些設置的值都是在 The Application Domain 下的.固然也提供了修改其餘Domain下的值的方法,只是須要總體的設置。
Mac上自帶安裝了SQLite3 ,若是你以前接觸過關係型數據庫,你能夠經過命令行來對SQLite進行初步的認識
$ sqlite3 test.db SQLite version 3.7.5 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite>create table if not exists names(id integer primary key asc, name text); sqlite> insert into names(name) values('Luke'); sqlite> select * from names; 1|Luke sqlite>
那若是在代碼中使用SQLite呢?
這樣以後你即可以經過C的接口來操做數據庫了
qlite3 *database;//sqlite3的類型其實只是一個結構體struct NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory , NSUserDomainMask , YES); NSString *databaseFilePath=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:@"luke.db"]; //打開數據庫 if (sqlite3_open([databaseFilePath UTF8String], &database)==SQLITE_OK) { NSLog(@"open sqlite db ok."); char *errorMsg; const char *createSql="create table if not exists names (id integer primary key asc,name text)"; //建立表 if (sqlite3_exec(database, createSql, NULL, NULL, &errorMsg)==SQLITE_OK) { NSLog(@"create ok."); }else { NSLog(@"error: %s",errorMsg); sqlite3_free(errorMsg); } //插入數據 const char *insertSql="insert into names (name) values(\"Luke\")"; if (sqlite3_exec(database, insertSql, NULL, NULL, &errorMsg) == SQLITE_OK) { NSLog(@"insert ok."); }else { NSLog(@"error: %s",errorMsg); sqlite3_free(errorMsg); } const char *selectSql="select id,name from names"; sqlite3_stmt *statement; if (sqlite3_prepare_v2(database, selectSql, -1, &statement, nil) == SQLITE_OK) { NSLog(@"select ok."); } while (sqlite3_step(statement)==SQLITE_ROW) { int _id=sqlite3_column_int(statement, 0); char *name=(char *)sqlite3_column_text(statement, 1); NSString *nameString = [NSString stringWithCString:name encoding:NSUTF8StringEncoding]; NSLog(@"row>>id %i, name %@",_id,nameString); } sqlite3_finalize(statement); } sqlite3_close(database);
你會發現這徹底是C語言編程,和Objective-C的代碼混在一塊兒格格不入,也很不方便,因此便有人開發了開源的sqlite c接口的wrapper
具體的使用方法,各自的文檔都寫的比較清楚。 FMDB不支持多線程同時使用同一個數據庫鏈接進行操做,不然會有線程安全問題,有可能致使數據庫文件損壞。EGODatabase則引入了多線程的支持,部分代碼借鑑了FMDB,二者在使用上很是的類似。另EGODatabase提供了異步數據庫操做的支持,將數據庫操做封裝成數據庫請求(其繼承於NSOperation),數據庫請求建立好了,丟到一個OperationQueue中被異步的進行執行,當請求數據完成以後 ,相應的delegate方法會被調用,而後你能夠在主線程更新顯示了.