iOS 文件操作簡介

級別: ★★☆☆☆
標(biāo)簽:「iOS 文件操作」「SandBox」「QiFileManager」
作者: dac_1033
審校: QiShare團(tuán)隊

在iOS開發(fā)過程中,網(wǎng)絡(luò)出錯沒有返回正確數(shù)據(jù)時有發(fā)生,這時可以讀取本地數(shù)據(jù)展示給用戶來優(yōu)化用戶體驗,并且在網(wǎng)絡(luò)請求正常時及時更新這些本地數(shù)據(jù),這些本地數(shù)據(jù)一般都存在沙盒目錄的文件中,下面我們就來簡單介紹一下iOS開發(fā)中的文件操作。附:蘋果官方文件系統(tǒng)編程指南。

一、 關(guān)于iOS文件系統(tǒng)

1.1 沙盒(SandBox)

iOS中每個app都有個獨(dú)立、封閉、安全的目錄,叫做沙盒,它一般存放著程序包文件(可執(zhí)行文件)、圖片、音頻、視頻、plist文件、SQLite數(shù)據(jù)庫以及其他文件。每個app的沙盒都是獨(dú)立的,應(yīng)用程序之間是不可以直接互相訪問的。蘋果官網(wǎng)將沙盒結(jié)構(gòu)劃分為三類(Bundle ContainerData ContaineriClound Container),如下圖:

app沙盒的結(jié)構(gòu)

上圖中的沙盒結(jié)構(gòu)其實(shí)是與小編的理解有出入的,具體的沙盒結(jié)構(gòu)我們暫且不做研究。在開發(fā)iOS應(yīng)用程序過程中,我們只能看到沙盒中的根目錄及默認(rèn)生成的三個文件夾DocumentsLibraryTmp,這些才是我們關(guān)注的重點(diǎn), 關(guān)于常用目錄的描述如下:

目錄 描述
AppName.app 應(yīng)用程序的程序包目錄,包含應(yīng)用程序的本身。由于應(yīng)用程序必須經(jīng)過簽名,所以不能在運(yùn)行時對這個目錄中的內(nèi)容進(jìn)行修改,否則會導(dǎo)致應(yīng)用程序無法啟動。
Documents/ 保存應(yīng)用程序的重要數(shù)據(jù)文件和用戶數(shù)據(jù)文件等。用戶數(shù)據(jù)基本上都放在這個位置(例如從網(wǎng)上下載的圖片或音樂文件),該文件夾在應(yīng)用程序更新時會自動備份,在連接iTunes時也可以自動同步備份其中的數(shù)據(jù);
該目錄的內(nèi)容被iTunes和iCloud備份。
Library/ 這個目錄下有兩個子目錄,可創(chuàng)建子文件夾。可以用來放置您希望被備份但不希望被用戶看到的數(shù)據(jù)。該路徑下的文件夾,除Caches以外,都會被iTunes備份;
該目錄的內(nèi)容被iTunes和iCloud備份
Library/Caches 保存應(yīng)用程序使用時產(chǎn)生的支持文件和緩存文件(保存應(yīng)用程序再次啟動過程中需要的信息),還有日志文件最好也放在這個目錄;
iTunes 不會備份該目錄,并且該目錄下數(shù)據(jù)可能被其他工具清理掉。
Library/Preferences 保存應(yīng)用程序的偏好設(shè)置文件。NSUserDefaults類創(chuàng)建的數(shù)據(jù)和plist文件都放在這里;
該目錄的內(nèi)容被iTunes和iCloud備份。
Tmp/ 使用此目錄可以編寫在應(yīng)用程序啟動之間不需要保留的臨時文件,您的應(yīng)用程序應(yīng)在不再需要時刪除此目錄中的文件,但是,當(dāng)您的應(yīng)用未運(yùn)行時,系統(tǒng)可能會清除此目錄;
iTunes或iCloud不會備份此目錄下的內(nèi)容。
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 常用的路徑處理方法

系統(tǒng)在類NSPathUtilities中實(shí)現(xiàn)了對NSString的擴(kuò)展NSString (NSStringPathExtensions),其中定義了很多方法專門用于處理路徑:

- (NSArray *)pathComponents;
- (NSString *)lastPathComponent;
- (NSString *)stringByDeletingLastPathCpmponent;
- (NSString *)stringByAppendingPathConmponent:(NSString *)str;
- (NSString *)pathExtension;
- (NSString *)stringByDeletingPathExtension;
- (NSString *)stringByAppendingPathExtension:(NSString *)str;
1.4 iOS工程中的NSBundle
工程中的bundle文件

如圖,我們自己在工程中創(chuàng)建了一個Test.bundlebundle是一個目錄,其中包含程序會使用到的資源(如圖像、聲音、代碼文件、nib文件等)。在系統(tǒng)中app本身和其他文件沒有什么區(qū)別,app包中實(shí)際上包含了nib文件、編譯連接過的代碼和其他資源的目錄,我們把a(bǔ)pp包這個目錄叫做該app的main bundle。在iOS開發(fā)中可以使用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);
    
// 直接根據(jù)目錄獲取資源
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;
}

// 創(chuàng)建路徑
+ (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) {
        // 注:直接創(chuàng)建不會覆蓋原文件夾
        [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
    }
}

// 創(chuàng)建文件
+ (NSString *)createFile:(NSString *)filePath fileName:(NSString *)fileName {
    
    // 先創(chuàng)建路徑
    [self createDirectory:filePath];
    
    // 再創(chuàng)建路徑上的文件
    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) {
        // 注:直接創(chuàng)建會被覆蓋原文件
        [fileManager createFileAtPath:path contents:nil attributes:nil];
    }
    return path;
}

// 復(fù)制 文件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);
    }
}

// 獲取目錄下所有內(nèi)容
+ (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 能直接進(jìn)行文件讀寫的數(shù)據(jù)類型

iOS中有四種簡單類型能夠直接進(jìn)行文件讀寫:字符串NSString/NSMutableString、數(shù)組NSArray/NSMutableArray、字典NSDictionary/NSMutableDictionary、二進(jìn)制數(shù)據(jù)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(@"讀取文件-數(shù)組: %@", 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(@"讀取文件-二進(jìn)制: %@", readData);
  • 其中數(shù)組和字典中的元素對象的類型也必須是上述四種,否則不能直接寫入文件;
  • 每次調(diào)用writeToFile:方法寫入文件時,都會覆蓋文件原有內(nèi)容。

三、文件內(nèi)容操作

iOS開發(fā)在涉及到文件操作的過程中,進(jìn)行一些細(xì)粒度的文件內(nèi)容操作時會用到NSFileHandle。一般用NSFileHandle修改文件內(nèi)容有三個步驟:打開文件,獲取一個NSFileHandle對象;對打開文件執(zhí)行相關(guān)操作;關(guān)閉文件。NSFileHandle具體功能概括如下:

  • 打開一個文件,執(zhí)行讀、寫或更新操作;
  • 在文件中查找指定位置;
  • 從文件中讀取特定數(shù)目的字節(jié),或?qū)⑻囟〝?shù)目的字節(jié)寫入文件中;
  • NSFileHandle類提供的方法也可以用于各種設(shè)備或套接字。

NSFileHandle操作文件內(nèi)容示例代碼如下:

// NSFileHandle操作文件內(nèi)容
- (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];
    
    // 讀取文件中指定位置/指定長度的內(nèi)容
    [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];
    // 注:直接覆蓋文件原有內(nèi)容
    [writeHandle writeData:data];
    
    // 注:覆蓋了指定位置/指定長度的內(nèi)容
    [writeHandle seekToFileOffset:2];
    [writeHandle writeData:[@"CDEFG" dataUsingEncoding:NSUTF8StringEncoding]];
    
    [writeHandle seekToEndOfFile];
    [writeHandle writeData:[@"一二三四五六" dataUsingEncoding:NSUTF8StringEncoding]];
    [writeHandle closeFile];
}

工程源碼地址:GitHub地址


推薦文章:
iOS 關(guān)鍵幀動畫
iOS 獲取設(shè)備當(dāng)前語言和地區(qū)
iOS 編寫高質(zhì)量Objective-C代碼(七)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,769評論 2 372