Objective-C 기초

아카이빙(Archiving)

nightohl 2019. 2. 16. 18:53
반응형

아카이빙(Archiving)

: 객체를 저장 / 복원 하는 기능.



1) XML 형태로 저장 / 복원

XML 형태로 plist (property list)를 저장.

plist 작성 방법은 NSArray 배열 객체를 만들고 객체들을 Array에 담은 후에 NSArray에서 제공하는 writeToFile 메소드로 파일로 보내면 됨.


★★특정 클래스만 가능 (커스텀 클래스를 저장하려하면 에러)

[제한된 클래스 종류]

NSStirng

NSArray

NSDictionary

NSDate

NSData

NSNumber

는 클래스 자체에서 제공하는 I/O기능으로 저장하면 됨.


ex) 실행파일(main.m)


#import <Foundation/Foundation.h>

#import "Cat.h"


int main(int argc, const char * argv[]) {

    @autoreleasepool {

        NSDictionary *dic= @{@"my_key":@"my_value"};

        NSString *str = @"My String !!";

        NSDate *date = [NSDate date];

        NSNumber *num = @1234;

        

        NSArray *my_array = @[dic,str,date,num];

        

        NSString *filePath = @"/Users/night-ohl/Desktop/my_objs";

        BOOL ret = [my_array writeToFile:filePath atomically:YES];

        NSLog(@"Result : %d", ret);

    }

    return 0;

}


==> 결과 : 

2019-02-16 16:00:16.503221+0900 Study[5717:320667] Result : 1

Program ended with exit code: 0


객체가 xml 형식으로 잘 저장된 것을 확인할 수 있음.


불러오기 과정은 생략.



2) 커스텀 클래스 인코딩/디코딩

Custom Class는 직렬화(Serialize) 방법으로 저장.

클래스를 직렬화 하려면 NSCoding 프로토콜에 선언된 인코딩/디코딩 메소드를 작성하여 사용.


[NSCoding 프로토콜 선언부 구성]

@protocol NSCoding

-(void)encodeWithCoder:(NSCoder *)aCoder; //인코딩 메소드

-(id)initWithCoder:(NSCoder *)aDecoder;        //디코딩 메소드

@end


NSCoding 프로토콜의 인코딩/디코딩 메소드의 구현은 인자로 받는 NSCoder 클래스의 메소드들을 이용하여 구현.


부모 클래스도 직렬화 하려면?

인코딩 메소드 구현 상단에 [super encodeWithCoder:aCoder]; 작성.

디코딩 메소드 구현 상단에 self = [super initWithCoder:aDecoder]; 작성.

(super 에서도 NSCoding 프로토콜의 인코딩/디코딩 메소드를 호출한다 라는 의미)


[NSCoder 클래스 구성]

주요 메소드만 살펴보면


(인코딩)

-(void)encodeInt:(int)into forKey(NSString *)key; //정수를 인코딩

-(void)encodeObject:(id)obj forKey:(NSString *)key; //객체를 인코딩


(디코딩)

-(int)decodeIntForKey:(NSStirng *)key;

-(id)decodeObjectForKey:(NSString *)key;


위 메소드를 이용하여 클래스 멤버 변수/프로퍼티 를 하나하나 '키-밸류' 방식으로 인코딩/디코딩


[지금까지의 과정 정리]

1) 인코딩 디코딩이 필요한 클래스에서 NSCoding 프로토콜을 채택.

2) NSCoding 프로토콜은 NSCoder 클래스를 인자로 받음.

3) NSCoding 프로토콜의 인코딩/디코딩 메소드를 -> NSCoder 클래스의 메소드들을 이용하여 구현.

4) NSCoding 프로토콜의 인코딩(encodeWithCoder) / 디코딩(initWithCoder) 메소드 준비 완료.


그렇다면 인코딩/디코딩 메소드는 언제 호출할까?

여기서 주의할 점은 우리가 정의한 인코딩/디코딩 메소드를 직접 사용하지 않고, '아카이브'를 통함.

다시말해, 지금까지 작성한 메소드는 인코딩/디코딩을 위한 준비만 했을 뿐이고, 실제 동작은 '아카이브'를 통함.



[아카이버]

아카이버(Archiver) 클래스가 준비된 인코딩/디코딩 메소드를 이용하여 객체들를 파일로 저장하거나 복원함.


iOS용 파운데이션에서 사용할 수 있는 아카이버 '클래스' 2가지

NSKeyedArchiver      : 클래스에 있는 내용을 (파일 혹은 NSData형태)로 저장.

NSKeyedUnarchiver    : (파일 혹은 NSData) 형태로 저장된 것을 객체 형태로 복원.

<NSCoding> 프로토콜을 채택하여 구현한 인코딩/디코딩 메소드가 아카이브 메소드를 통해 자동으로 동작함.


NSKeydArchiver 클래스

+(BOOL)archiveRootObject:(id)rootObject toFile:(NSString *)path; //객체를 파일로 저장.

NSKeyedUnarchiver 클래스

+(id)unarchiveObjectWithFile:(NSString *);

등등..


ex)

[Rectangle 클래스 선언부 Rectangle.h]


#import <Foundation/Foundation.h>


@interface Rectangle : NSObject <NSCoding> //NSCoding 프로토콜을 채택.


@property int width;

@property int height;


@end


[Rectangle 클래스 구현부 Rectangle.m]


#import "Rectangle.h"


@implementation Rectangle


-(void)encodeWithCoder:(NSCoder *)aCoder{ //NSCoding 프로토콜의 인코딩 메소드 작성

    [aCoder encodeInt:_width forKey:@"WIDTH"];

    [aCoder encodeInt:_height forKey:@"HEIGHT"];

}


//저장된 객체를 복원

-(id)initWithCoder:(NSCoder *)aDecoder{ //NSCoding 프로토콜의 디코딩 메소드 작성

    self = [super init];

    if(self){

        //디코딩 후 멤버변수를 초기화하는 과정

        _width = [aDecoder decodeIntForKey:@"WIDTH"];

        _height = [aDecoder decodeIntForKey:@"HEIGHT"];

    }

    return self;

}


-(NSString *)description{

    return [NSString stringWithFormat:@"사각형 - 가로,세로 (%d, %d)\n", _width, _height];

}


@end


[실행 파일 main.m]


#import <Foundation/Foundation.h>

#import "Rectangle.h"


int main(int argc, const char * argv[]) {

    @autoreleasepool {

        Rectangle *rec = [[Rectangle alloc] init];

        rec.width = 10;

        rec.height = 20;

        NSLog(@"Rec1 : %@",rec);

        

        NSString *filePath = @"/Users/night-ohl/Desktop/archiving";

        BOOL ret = [NSKeyedArchiver archiveRootObject:rec toFile:filePath];

        NSLog(@"Ret: %d",ret);

        

        Rectangle *rec2 = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];

        NSLog(@"Rec2 : %@",rec2);

    }

    return 0;

}


==> 결과 :

아카이버 호출 후 archiving 객체가 인코딩 되어 파일로 저장된 모습.


2019-02-18 11:29:01.833929+0900 Study[8084:1182913] Rec1 : 사각형 - 가로,세로 (10, 20) 객체생성

2019-02-18 11:29:01.835339+0900 Study[8084:1182913] Ret: 1 객체를 인코딩하여 파일로 저장

2019-02-18 11:29:01.835881+0900 Study[8084:1182913] Rec2 : 사각형 - 가로,세로 (10, 20) 파일로부터 디코딩하여 객체 복원

Program ended with exit code: 0



바이너리 데이터인 NSData 객체를 저장/복원 하는 내용은 다음 글에서 정리함.


반응형

'Objective-C 기초' 카테고리의 다른 글

블록 (Block)  (0) 2019.02.18
NSData 다루기 (바이너리 데이터 저장/복원)  (0) 2019.02.18
프로토콜 (Protocol)  (0) 2019.02.15
클래스 내부 구성요소 숨기기  (0) 2019.02.15
카테고리 Category  (0) 2019.02.15