iOS 檔案操作簡介
級別: ★★☆☆☆
標籤:「iOS 檔案操作」「SandBox」「QiFileManager」
作者:dac_1033
審校:QiShare團隊
在iOS開發過程中,網路出錯沒有返回正確資料時有發生,這時可以讀取本地資料展示給使用者來優化使用者體驗,並且在網路請求正常時及時更新這些本地資料,這些本地資料一般都存在沙盒目錄的檔案中,下面我們就來簡單介紹一下iOS開發中的檔案操作。 ofollow,noindex">附:蘋果官方檔案系統程式設計指南。
一、 關於iOS檔案系統
1.1 沙盒(SandBox)
iOS中每個app都有個獨立、封閉、安全的目錄,叫做沙盒,它一般存放著程式包檔案(可執行檔案)、圖片、音訊、視訊、 plist
檔案、 SQLite
資料庫以及其他檔案。每個app的沙盒都是獨立的,應用程式之間是不可以直接互相訪問的。蘋果官網將沙盒結構劃分為三類( Bundle Container
、 Data Container
、 iClound Container
),如下圖:

上圖中的沙盒結構其實是與小編的理解有出入的,具體的沙盒結構我們暫且不做研究。在開發iOS應用程式過程中,我們只能看到沙盒中的根目錄及預設生成的三個資料夾 Documents
、 Library
和 Tmp
,這些才是我們關注的重點, 關於常用目錄的描述如下:
目錄 | 描述 |
---|---|
AppName.app | 應用程式的程式包目錄,包含應用程式的本身。由於應用程式必須經過簽名,所以不能在執行時對這個目錄中的內容進行修改,否則會導致應用程式無法啟動。 |
Documents/ | 儲存應用程式的重要資料檔案和使用者資料檔案等。使用者資料基本上都放在這個位置(例如從網上下載的圖片或音樂檔案),該資料夾在應用程式更新時會自動備份,在連線iTunes時也可以自動同步備份其中的資料; 該目錄的內容被iTunes和iCloud備份。 |
Library/ | 這個目錄下有兩個子目錄,可建立子資料夾。可以用來放置您希望被備份但不希望被使用者看到的資料。該路徑下的資料夾,除Caches以外,都會被iTunes備份; 該目錄的內容被iTunes和iCloud備份 |
Library/Caches | 儲存應用程式使用時產生的支援檔案和快取檔案(儲存應用程式再次啟動過程中需要的資訊),還有日誌檔案最好也放在這個目錄; iTunes 不會備份該目錄,並且該目錄下資料可能被其他工具清理掉。 |
Library/Preferences | 儲存應用程式的偏好設定檔案。NSUserDefaults類建立的資料和plist檔案都放在這裡; 該目錄的內容被iTunes和iCloud備份。 |
Tmp/ | 使用此目錄可以編寫在應用程式啟動之間不需要保留的臨時檔案,您的應用程式應在不再需要時刪除此目錄中的檔案,但是,當您的應用未執行時,系統可能會清除此目錄; iTunes或iCloud不會備份此目錄下的內容。 |
1.2 獲取沙盒目錄
- (void)testSandBoxDirectory { // 獲取app沙盒的根目錄(home) NSString *homePath = NSHomeDirectory(); NSLog(@"NSHomeDirectory: %@", homePath); // 獲取temp路徑 NSString *tmp = NSTemporaryDirectory( ); NSLog(@"NSTemporaryDirectory: %@", tmp); // 獲取Document目錄 NSArray*paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *docPath = [paths lastObject]; NSLog(@"NSDocumentDirectory: %@", docPath); // 獲取Library目錄 paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString *libPath = [paths lastObject]; NSLog(@"NSLibraryDirectory: %@", libPath); // 獲取Library中的Cache paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *cachesPath = [paths lastObject]; NSLog(@"NSCachesDirectory: %@", cachesPath); } 複製程式碼
1.3 常用的路徑處理方法
系統在類 NSPathUtilities
中實現了對NSString的擴充套件NSString ( NSStringPathExtensions
),其中定義了很多方法專門用於處理路徑:
- (NSArray *)pathComponents; - (NSString *)lastPathComponent; - (NSString *)stringByDeletingLastPathCpmponent; - (NSString *)stringByAppendingPathConmponent:(NSString *)str; - (NSString *)pathExtension; - (NSString *)stringByDeletingPathExtension; - (NSString *)stringByAppendingPathExtension:(NSString *)str; 複製程式碼
1.4 iOS工程中的NSBundle

如圖,我們自己在工程中建立了一個 Test.bundle
, bundle
是一個目錄,其中包含程式會使用到的資源(如影象、聲音、程式碼檔案、 nib
檔案等)。在系統中app本身和其他檔案沒有什麼區別,app包中實際上包含了 nib
檔案、編譯連線過的程式碼和其他資源的目錄,我們把app包這個目錄叫做該app的 main bundle
。在iOS開發中可以使用NSBundle類來操作bundle及其中的資源。 NSBundle的官方文件描述
使用NSBundle的示例程式碼如下:
// 獲取main bundle NSBundle *mainBundle = [NSBundle mainBundle]; // 放在app mainBundle中的自定義Test.bundle NSString *testBundlePath = [mainBundle pathForResource:@"Test" ofType:@"bundle"]; NSBundle *testBundle = [NSBundle bundleWithPath:testBundlePath]; // 獲取Test.bundle中資源 NSString *resPath = [testBundle pathForResource:@"sound02" ofType:@"wav"]; NSLog(@"自定義bundle中資源的路徑: %@", resPath); // 直接根據目錄獲取資源 UIImage *img = [UIImage imageNamed:[NSString stringWithFormat:@"Test.bundle/%@", @"logo_img_02"]]; NSLog(@"自定義bundle中圖片: %@", img); 複製程式碼
二、檔案操作
2.1 檔案路徑及檔案
// 獲取沙盒根路徑 + (NSString *)getHomePath { return NSHomeDirectory(); } // 獲取tmp路徑 + (NSString *)getTmpPath { return NSTemporaryDirectory(); } // 獲取Documents路徑 + (NSString *)getDocumentsPath { NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *path = [pathArr firstObject]; return path; } // 獲取Library路徑 + (NSString *)getLibraryPath { NSArray *pathArr = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); NSString *path = [pathArr firstObject]; return path; } // 獲取LibraryCache路徑 + (NSString *)getLibraryCachePath { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); NSString *path = [paths firstObject]; return path; } // 檢查檔案、資料夾是否存在 + (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(BOOL *)isDir { NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL exist = [fileManager fileExistsAtPath:path isDirectory:isDir]; return exist; } // 建立路徑 + (void)createDirectory:(NSString *)path { NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDir; BOOL exist = [fileManager fileExistsAtPath:path isDirectory:&isDir]; if (!isDir) { [fileManager removeItemAtPath:path error:nil]; exist = NO; } if (!exist) { // 注:直接建立不會覆蓋原資料夾 [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; } } // 建立檔案 + (NSString *)createFile:(NSString *)filePath fileName:(NSString *)fileName { // 先建立路徑 [self createDirectory:filePath]; // 再建立路徑上的檔案 NSFileManager *fileManager = [NSFileManager defaultManager]; NSString *path = [filePath stringByAppendingPathComponent:fileName]; BOOL isDir; BOOL exist = [fileManager fileExistsAtPath:path isDirectory:&isDir]; if (isDir) { [fileManager removeItemAtPath:path error:nil]; exist = NO; } if (!exist) { // 注:直接建立會被覆蓋原檔案 [fileManager createFileAtPath:path contents:nil attributes:nil]; } return path; } // 複製 檔案or資料夾 + (void)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath { NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; BOOL result = [fileManager copyItemAtPath:srcPath toPath:dstPath error:&error]; if (!result && error) { NSLog(@"copyItem Err : %@", error.description); } } // 移動 檔案or資料夾 + (void)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath { NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; BOOL result = [fileManager moveItemAtPath:srcPath toPath:dstPath error:&error]; if (!result && error) { NSLog(@"moveItem Err : %@", error.description); } } // 刪除 檔案or資料夾 + (void)removeItemAtPath:(NSString *)path { NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; BOOL result = [fileManager removeItemAtPath:path error:&error]; if (!result && error) { NSLog(@"removeItem Err : %@", error.description); } } // 獲取目錄下所有內容 + (NSArray *)getContentsOfDirectoryAtPath:(NSString *)docPath { NSFileManager *fileManager = [NSFileManager defaultManager]; NSError *error; NSArray *contentArr = [fileManager contentsOfDirectoryAtPath:docPath error:&error]; if (!contentArr.count && error) { NSLog(@"ContentsOfDirectory Err : %@", error.description); } return contentArr; } 複製程式碼
2.2 能直接進行檔案讀寫的資料型別
iOS中有四種簡單型別能夠直接進行檔案讀寫:字串 NSString
/ NSMutableString
、陣列 NSArray
/ NSMutableArray
、字典 NSDictionary
/ NSMutableDictionary
、二進位制資料 NSData
/ NSMutableData
。
程式碼示例如下:
NSString *string = @"QiShare test string ..."; [string writeToFile:path11 atomically:YES encoding:NSUTF8StringEncoding error:nil]; NSString *readStr = [NSString stringWithContentsOfFile:path11 encoding:NSUTF8StringEncoding error:nil]; NSLog(@"讀取檔案-字串: %@", readStr); NSArray *array = @[@"Q", @"i", @"S", @"h", @"a", @"r", @"e"]; [array writeToFile:path33 atomically:YES]; NSArray *readArr = [NSArray arrayWithContentsOfFile:path33]; NSLog(@"讀取檔案-陣列: %@", readArr); NSDictionary *dict = @{@"en":@"QiShare", @"ch":[[FileUtil alloc] init]}; [dict writeToFile:path34 atomically:YES]; NSDictionary *readDict = [NSDictionary dictionaryWithContentsOfFile:path34]; NSLog(@"讀取檔案-字典: %@", readDict); NSData *data = [@"QiShare test data ..." dataUsingEncoding:NSUTF8StringEncoding]; [data writeToFile:path11 atomically:YES]; NSData *readData = [NSData dataWithContentsOfFile:path11]; NSLog(@"讀取檔案-二進位制: %@", readData); 複製程式碼
- 其中陣列和字典中的元素物件的型別也必須是上述四種,否則不能直接寫入檔案;
- 每次呼叫writeToFile:方法寫入檔案時,都會覆蓋檔案原有內容。
三、檔案內容操作
iOS開發在涉及到檔案操作的過程中,進行一些細粒度的檔案內容操作時會用到NSFileHandle。一般用NSFileHandle修改檔案內容有三個步驟:開啟檔案,獲取一個NSFileHandle物件;對開啟檔案執行相關操作;關閉檔案。NSFileHandle具體功能概括如下:
- 開啟一個檔案,執行讀、寫或更新操作;
- 在檔案中查詢指定位置;
- 從檔案中讀取特定數目的位元組,或將特定數目的位元組寫入檔案中;
- NSFileHandle類提供的方法也可以用於各種裝置或套接字。
用 NSFileHandle
操作檔案內容示例程式碼如下:
// NSFileHandle操作檔案內容 - (void)testFileHandle { NSString *docPath = [FileUtil getDocumentsPath]; NSString *readPath = [docPath stringByAppendingPathComponent:@"read.txt"]; NSString *writePath = [docPath stringByAppendingPathComponent:@"write.txt"]; NSData *data = [@"abcdefghijklmnopqrstuvwxyz" dataUsingEncoding:NSUTF8StringEncoding]; NSFileManager *manager=[NSFileManager defaultManager]; [manager createFileAtPath:readPath contents:data attributes:nil]; [manager createFileAtPath:writePath contents:nil attributes:nil]; [data writeToFile:readPath atomically:YES]; // 開啟檔案 讀 NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingAtPath:readPath]; NSData *readData = [readHandle readDataToEndOfFile]; // 讀取檔案中指定位置/指定長度的內容 [readHandle seekToFileOffset:10]; readData = [readHandle readDataToEndOfFile]; NSLog(@"seekToFileOffset:10 = %@", [[NSString alloc] initWithData:readData encoding:NSUTF8StringEncoding]); [readHandle seekToFileOffset:10]; readData = [readHandle readDataOfLength:5]; NSLog(@"seekToFileOffset:10 = %@",[[NSString alloc]initWithData:readData encoding:NSUTF8StringEncoding]); [readHandle closeFile]; // 開啟檔案 寫 NSFileHandle *writeHandle = [NSFileHandle fileHandleForWritingAtPath:writePath]; // 注:直接覆蓋檔案原有內容 [writeHandle writeData:data]; // 注:覆蓋了指定位置/指定長度的內容 [writeHandle seekToFileOffset:2]; [writeHandle writeData:[@"CDEFG" dataUsingEncoding:NSUTF8StringEncoding]]; [writeHandle seekToEndOfFile]; [writeHandle writeData:[@"一二三四五六" dataUsingEncoding:NSUTF8StringEncoding]]; [writeHandle closeFile]; } 複製程式碼
工程原始碼地址: GitHub地址
關注我們的途徑有:
QiShare(微信公眾號)