2012-01-20 8 views
6

オフラインで使用するために保存したいオブジェクトがたくさんあります。 現在、オブジェクト用にNSCoder準拠のクラスを作成し、オフラインで利用できるようにファイルにコード化したデータを使用しています。私は、オブジェクトを導入した.hでそうNSCoder対応クラスの簡単な方法

@interface MyClass : NSObject<NSCoding>{ 
NSNumber* myObject;} 
@property(nonatomic,retain) NSNumber* myObject; 

そして.Mで、私はのINITを行います

- (id) initWithCoder: (NSCoder *)coder { 
    if (self = [super init]) { 
     [self setMyObject: [coder decodeObjectForKey:@"myObject"]]; 
    } 
} 

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

だから、クラスがゲッターとセッターを持つだけでダミーのストレージです。 デコード/エンコードを行うより良い方法はここにあります。 エンコードとデコードに何らかの@ダイナミックまたはキー値コーディングを使用できますか? 基本的には、クラスのすべての変数をファイルに保存し、プログラムが起動するときにオブジェクトに戻したいと思っています。 このアプローチは機能しますが、すべてのクラスを作成するのには時間と労力がかかります。

答えて

43

はい、これは自動的に実行できます。

- (NSArray *)propertyKeys 
{ 
    NSMutableArray *array = [NSMutableArray array]; 
    Class class = [self class]; 
    while (class != [NSObject class]) 
    { 
     unsigned int propertyCount; 
     objc_property_t *properties = class_copyPropertyList(class, &propertyCount); 
     for (int i = 0; i < propertyCount; i++) 
     { 
      //get property 
      objc_property_t property = properties[i]; 
      const char *propertyName = property_getName(property); 
      NSString *key = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding]; 

      //check if read-only 
      BOOL readonly = NO; 
      const char *attributes = property_getAttributes(property); 
      NSString *encoding = [NSString stringWithCString:attributes encoding:NSUTF8StringEncoding]; 
      if ([[encoding componentsSeparatedByString:@","] containsObject:@"R"]) 
      { 
       readonly = YES; 

       //see if there is a backing ivar with a KVC-compliant name 
       NSRange iVarRange = [encoding rangeOfString:@",V"]; 
       if (iVarRange.location != NSNotFound) 
       { 
        NSString *iVarName = [encoding substringFromIndex:iVarRange.location + 2]; 
        if ([iVarName isEqualToString:key] || 
         [iVarName isEqualToString:[@"_" stringByAppendingString:key]]) 
        { 
         //setValue:forKey: will still work 
         readonly = NO; 
        } 
       } 
      } 

      if (!readonly) 
      { 
       //exclude read-only properties 
       [array addObject:key]; 
      } 
     } 
     free(properties); 
     class = [class superclass]; 
    } 
    return array; 
} 

そして、ここにあなたのNSCoderのメソッドです:

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    if ((self = [self init])) 
    { 
     for (NSString *key in [self propertyKeys]) 
     { 
      id value = [aDecoder decodeObjectForKey:key]; 
      [self setValue:value forKey:key]; 
     } 
    } 
    return self; 
} 

- (void)encodeWithCoder:(NSCoder *)aCoder 
{ 
    for (NSString *key in [self propertyKeys]) 
    { 
     id value = [self valueForKey:key]; 
     [aCoder encodeObject:value forKey:key]; 
    } 
} 

#import <objc/runtime.h> 
#import <objc/message.h> 

今すぐプロパティ名を取得するには、低レベルのメソッドを使用します。このメソッドを追加:ファーストクラスにこれらをインポートします

これには少し注意する必要があります。

  1. これは数値、bool、オブジェクトなどのプロパティで機能しますが、カスタム構造体は機能しません。また、クラス内のいずれかのプロパティが、NSCodingをサポートしていないオブジェクトである場合、これは機能しません。

  2. これは、ivarsではなく合成されたプロパティでのみ機能します。

あなたはそれをコードする、またはそれ以上の優雅な問題を処理するためにsetValueForUndefinedKeyメソッドをオーバーライドする前にencodeWithCoderにおける値の型をチェックすることにより、エラー処理を追加することができます。

UPDATE:

私はライブラリにこれらのメソッドを包んました:https://github.com/nicklockwood/AutoCoding - ライブラリはので、任意のクラスが保存またはロードすることができNSObjectの上のカテゴリーとしてこれらのメソッドを実装し、それはまた、継承された符号化のためのサポートを追加しますプロパティ、私の元の答えは処理されません。

UPDATE 2:

私が正しく継承され、読み取り専用プロパティに対処するための答えを更新しました。

+0

偉大な答えをありがとう、これは私の目的には完璧です。 私はあなたに投票しますが、私は十分な評判を持っていません。 – MapaX

+0

コードに小さなメモリリークがあります。 追加する必要があります。 無料(プロパティ); propertyKeysメソッドへの – MapaX

+0

良いコール - 私はそれを修正しました。 –

0

ライブラリhttps://github.com/nicklockwood/AutoCodingは、毎回class_copyPropertyListを使用したリフレクションを行っているため、パフォーマンスが悪く、一部の構造体をサポートしていないようです。

チェックhttps://github.com/flexme/DYCodingは、あらかじめコンパイルされたコードと同じ速さでなければならず、さまざまなプロパティタイプをサポートしています。