在 Objective-C 將自訂物件陣列儲存到檔案

KeyedArchiverDemo

將陣列資料儲存到檔案中。

儲存陣列資料

在 Objective-C 中,將陣列資料儲存到本地端的檔案非常容易,如下:
NSArray *demo = @[@"ABC", @123];
[demo writeToFile:[self filePathOfDocument:@"demo.plist"] atomically:YES];
如此就會在 App 的 Documents 目錄,建立 demo.plist ,內容如下:
<?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">
<array>
    <string>ABC</string>
    <integer>123</integer>
</array>
</plist>
要從檔案載入也很簡單,如下:
NSArray *demo = [NSArray arrayWithContentsOfFile:[self filePathOfDocument:@"demo.plist"]];
但是,這個方法只能儲存 Objective-C 內建的物件,例如 NSString、NSInteger 等,如果是自訂的物件就無法使用。

將自定物件陣列儲存到檔案

要儲存自訂的物件陣列,必須用 NSKeyedArchiver 來封裝,並使用 NSKeyedUnarchiver 來解開。要讓這兩個類類發生作用,必須在自訂物件的類別中加入如何封裝的程式碼。

假設我們有一個使用者類別 User.h 有兩個屬性:
#import <Foundation/Foundation.h>

@interface User : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end
在 User.m 中加入封裝用方法:
#import "User.h"

NSString *const KADUserName = @"name";
NSString *const KADUserAge  = @"age";

@implementation User

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        _name = [coder decodeObjectForKey:KADUserName];
        _age = [coder decodeIntegerForKey:KADUserAge];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    if ([coder isKindOfClass:[NSKeyedArchiver class]]) {
        [coder encodeObject:_name forKey:KADUserName];
        [coder encodeInteger:_age forKey:KADUserAge];
    } else {
        [NSException raise:NSInvalidArchiveOperationException
                format:@"Only supports NSKeyedArchiver coders"];
    }
}

@end
initWithCoder 用來解封裝,encodeWithCoder: 則是用來封裝。

這樣 User 類別所建立的物件就能被儲存成檔案。
使用範例如下:
- (void)viewDidLoad
{
    [super viewDidLoad];

    NSLog(@"建立使用者");
    User *user1 = [User new];
    user1.name = @"Tony";
    user1.age = 18;

    User *user2 = [User new];
    user2.name = @"Ava";
    user2.age = 22;

    User *user3 = [User new];
    user3.name = @"Karen";
    user3.age = 26;

    NSArray *users = @[user1, user2, user3];

    NSLog(@"儲存使用者");
    [self saveUserToFile:users];

    NSLog(@"載入使用者");
    NSArray *loadUsers = [self loadUserFromFile];

    NSLog(@"顯示使用者");
    for (User *user in loadUsers) {
        NSLog(@"User Name: %@, age:%i", user.name, user.age);
    }
}

- (void)saveUserToFile:(NSArray *)users
{
    NSString *filePath = [self filePathOfDocument:KADUserPlist];
    [NSKeyedArchiver archiveRootObject:users toFile:filePath];
}

- (NSArray *)loadUserFromFile
{
    NSString *filePath = [self filePathOfDocument:KADUserPlist];
    return [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
}

/** 
 取得文件目錄路徑(Documents) 
 */
- (NSString *)filePathOfDocument:(NSString *)filename
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docDir = [paths objectAtIndex:0];

    return [docDir stringByAppendingPathComponent:filename];
}
另外,在檔案前面還建立了一個檔名常數,
NSString *const KADUserPlist = @"user.plist";
執行結果就如文章一開頭的圖片所示。使用 Finder 開啟 App 的 Documents 目錄,就會發現被建立了一個 user.plist 檔。

參考資料

本文網址:http://blog.tonycube.com/2014/06/objective-c.html
Tony Blog 撰寫,轉載時請註明出處及文章連結,謝謝 😀

我要留言

留言小提醒:
1.回覆時間通常在晚上,如果太忙可能要等幾天。
2.請先瀏覽一下其他人的留言,也許有人問過同樣的問題。
3.程式碼請先將它編碼後再貼上。(線上編碼:http://bit.ly/1DL6yog)
4.文字請加上標點符號及斷行,難以閱讀者恕難回覆。
5.感謝您的留言,您的問題也可能幫助到其他有相同問題的人。