[cocoa-touch] NSUserDefaults의 NSMutableArray에 사용자 지정 개체 저장

최근에 내 iPhone 앱의 검색 결과를 NSUserDefaults 컬렉션에 저장하려고했습니다. 또한 이것을 사용하여 사용자 등록 정보를 성공적으로 저장하지만 어떤 이유로 사용자 지정 위치 클래스의 NSMutableArray를 저장하려고하면 항상 비어있게됩니다.

이 게시물에서 NSMutableArray를 NSData 요소로 변환하려고 시도했지만 동일한 결과를 얻었습니다 ( iPhone에서 NSUserDefaults를 사용하여 정수 배열을 저장할 수 있습니까? ).

내가 시도한 코드 샘플은 다음과 같습니다.

저장:

[prefs setObject:results forKey:@"lastResults"];
[prefs synchronize];

또는

NSData *data = [NSData dataWithBytes:&results length:sizeof(results)];
[prefs setObject:data forKey:@"lastResults"];

또는

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:results];
[prefs setObject:data forKey:@"lastResults"];

하중:

lastResults = (NSMutableArray *)[prefs objectForKey:@"lastResults"];

또는

NSData *data = [prefs objectForKey:@"lastResults"];
memcpy(&lastResults, data.bytes, data.length);  

또는

NSData *data = [prefs objectForKey:@"lastResults"];
lastResults = [NSKeyedUnarchiver unarchiveObjectWithData:data];

아래의 조언을 따르면 내 개체에 NSCoder를 구현했습니다 (임시 NSString의 남용 무시).

#import "Location.h"


@implementation Location

@synthesize locationId;
@synthesize companyName;
@synthesize addressLine1;
@synthesize addressLine2;
@synthesize city;
@synthesize postcode;
@synthesize telephoneNumber;
@synthesize description;
@synthesize rating;
@synthesize priceGuide;
@synthesize latitude;
@synthesize longitude;
@synthesize userLatitude;
@synthesize userLongitude;
@synthesize searchType;
@synthesize searchId;
@synthesize distance;
@synthesize applicationProviderId;
@synthesize contentProviderId;

- (id) initWithCoder: (NSCoder *)coder
{
    if (self = [super init])
    {
        self.locationId = [coder decodeObjectForKey:@"locationId"];
        self.companyName = [coder decodeObjectForKey:@"companyName"];
        self.addressLine1 = [coder decodeObjectForKey:@"addressLine1"];
        self.addressLine2 = [coder decodeObjectForKey:@"addressLine2"];
        self.city = [coder decodeObjectForKey:@"city"];
        self.postcode = [coder decodeObjectForKey:@"postcode"];
        self.telephoneNumber = [coder decodeObjectForKey:@"telephoneNumber"];
        self.description = [coder decodeObjectForKey:@"description"];
        self.rating = [coder decodeObjectForKey:@"rating"];
        self.priceGuide = [coder decodeObjectForKey:@"priceGuide"];
        self.latitude = [coder decodeObjectForKey:@"latitude"];
        self.longitude = [coder decodeObjectForKey:@"longitude"];
        self.userLatitude = [coder decodeObjectForKey:@"userLatitude"];
        self.userLongitude = [coder decodeObjectForKey:@"userLongitude"];
        self.searchType = [coder decodeObjectForKey:@"searchType"];
        self.searchId = [coder decodeObjectForKey:@"searchId"];
        self.distance = [coder decodeObjectForKey:@"distance"];
        self.applicationProviderId = [coder decodeObjectForKey:@"applicationProviderId"];
        self.contentProviderId = [coder decodeObjectForKey:@"contentProviderId"];
    }
}

- (void) encodeWithCoder: (NSCoder *)coder
{
    [coder encodeObject:locationId forKey:@"locationId"];
    [coder encodeObject:companyName forKey:@"companyName"];
    [coder encodeObject:addressLine1 forKey:@"addressLine1"];
    [coder encodeObject:addressLine2 forKey:@"addressLine2"];
    [coder encodeObject:city forKey:@"city"];
    [coder encodeObject:postcode forKey:@"postcode"];
    [coder encodeObject:telephoneNumber forKey:@"telephoneNumber"];
    [coder encodeObject:description forKey:@"description"];
    [coder encodeObject:rating forKey:@"rating"];
    [coder encodeObject:priceGuide forKey:@"priceGuide"];
    [coder encodeObject:latitude forKey:@"latitude"];
    [coder encodeObject:longitude forKey:@"longitude"];
    [coder encodeObject:userLatitude forKey:@"userLatitude"];
    [coder encodeObject:userLongitude forKey:@"userLongitude"];
    [coder encodeObject:searchType forKey:@"searchType"];
    [coder encodeObject:searchId forKey:@"searchId"];
    [coder encodeObject:distance forKey:@"distance"];
    [coder encodeObject:applicationProviderId forKey:@"applicationProviderId"];
    [coder encodeObject:contentProviderId forKey:@"contentProviderId"];

}



답변

배열에 사용자 지정 개체를로드하는 경우 다음과 같이 배열을 가져 왔습니다.

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:@"savedArray"];
if (dataRepresentingSavedArray != nil)
{
    NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
    if (oldSavedArray != nil)
        objectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
    else
        objectArray = [[NSMutableArray alloc] init];
}

nil에서 아카이브를 해제하면 충돌이 발생한다고 믿기 때문에 사용자 기본값에서 반환 된 데이터가 nil이 아닌지 확인해야합니다.

보관은 다음 코드를 사용하여 간단합니다.

[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:objectArray] forKey:@"savedArray"];

f3lix가 지적했듯이 사용자 지정 개체가 NSCoding 프로토콜을 준수하도록해야합니다. 다음과 같은 메소드를 추가하면 트릭을 수행 할 수 있습니다.

- (void)encodeWithCoder:(NSCoder *)coder;
{
    [coder encodeObject:label forKey:@"label"];
    [coder encodeInteger:numberID forKey:@"numberID"];
}

- (id)initWithCoder:(NSCoder *)coder;
{
    self = [super init];
    if (self != nil)
    {
        label = [[coder decodeObjectForKey:@"label"] retain];
        numberID = [[coder decodeIntegerForKey:@"numberID"] retain];
    }
    return self;
}


답변

initWithCoder 메서드에서 오류가 발생했다고 생각합니다. 최소한 제공된 코드에서는 ‘self’개체를 반환하지 않습니다.

- (id) initWithCoder: (NSCoder *)coder
{
    if (self = [super init])
    {
        self.locationId = [coder decodeObjectForKey:@"locationId"];
        self.companyName = [coder decodeObjectForKey:@"companyName"];
        self.addressLine1 = [coder decodeObjectForKey:@"addressLine1"];
        self.addressLine2 = [coder decodeObjectForKey:@"addressLine2"];
        self.city = [coder decodeObjectForKey:@"city"];
        self.postcode = [coder decodeObjectForKey:@"postcode"];
        self.telephoneNumber = [coder decodeObjectForKey:@"telephoneNumber"];
        self.description = [coder decodeObjectForKey:@"description"];
        self.rating = [coder decodeObjectForKey:@"rating"];
        self.priceGuide = [coder decodeObjectForKey:@"priceGuide"];
        self.latitude = [coder decodeObjectForKey:@"latitude"];
        self.longitude = [coder decodeObjectForKey:@"longitude"];
        self.userLatitude = [coder decodeObjectForKey:@"userLatitude"];
        self.userLongitude = [coder decodeObjectForKey:@"userLongitude"];
        self.searchType = [coder decodeObjectForKey:@"searchType"];
        self.searchId = [coder decodeObjectForKey:@"searchId"];
        self.distance = [coder decodeObjectForKey:@"distance"];
        self.applicationProviderId = [coder decodeObjectForKey:@"applicationProviderId"];
        self.contentProviderId = [coder decodeObjectForKey:@"contentProviderId"];
    }

    return self; // this is missing in the example above


}

나는 사용한다

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:results];
[prefs setObject:data forKey:@"lastResults"];

NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
NSData *dataRepresentingSavedArray = [currentDefaults objectForKey:@"lastResults"];
if (dataRepresentingSavedArray != nil)
{
        NSArray *oldSavedArray = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedArray];
        if (oldSavedArray != nil)
                objectArray = [[NSMutableArray alloc] initWithArray:oldSavedArray];
        else
                objectArray = [[NSMutableArray alloc] init];
}

저에게 완벽하게 작동합니다.

안부,

스테판


답변

코드의 문제가 결과 배열을 저장하지 않는 것 같습니다. 데이터를로드하려면

lastResults = [prefs arrayForKey:@"lastResults"];

이것은 키로 지정된 배열을 반환합니다.


답변

“NSUserDefaults가 iPhone SDK에 NSMutableDictionary를 저장하지 못한 이유는 무엇입니까?”를 참조하십시오. ( NSUserDefaults가 iPhone SDK에 NSMutableDictionary를 저장하지 못한 이유는 무엇입니까? )


사용자 지정 개체를 (역) 직렬화하려면 데이터 (NSCoding 프로토콜)를 (역) 직렬화하는 함수를 제공해야합니다. 참조하는 솔루션은 배열이 객체가 아니라 연속적인 메모리 청크이기 때문에 int 배열과 함께 작동합니다.


답변

나는 이와 같은 것을 기본 db에 저장하지 않는 것이 좋습니다.

SQLite는 선택하고 사용하기가 매우 쉽습니다. 내 스크린 캐스트 중 하나 ( http://pragprog.com/screencasts/v-bdiphone )에 내가 작성한 간단한 래퍼에 대한 에피소드가 있습니다 (SC를 구입하지 않고도 코드를 얻을 수 있음).

앱 공간에 앱 데이터를 저장하는 것이 훨씬 깔끔합니다.

이 데이터를 기본값 db에 넣는 것이 여전히 타당하다면 게시 된 f3lix 게시물을 참조하십시오.


답변