6

オフラインで使用するために保存したいオブジェクトがたくさんあります。現在、オブジェクトとコード化されたデータをオフラインで使用できるように、NSCoder 準拠のクラスを作成しています。

したがって、.h でオブジェクトを紹介します。

@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"];
}

したがって、クラスは getter と setter を使用した単なるダミー ストレージです。デコード/エンコードを行うためのより良い方法はここにあります。エンコードとデコードに @dynamic または Key-value コーディングを使用できますか? 基本的に、クラス内のすべての変数をファイルに保存し、プログラムの起動時にオブジェクトに戻す必要があります。このアプローチは機能しますが、すべてのクラスを作成するには時間と労力がかかります。

4

2 に答える 2

43

はい、これは自動的に行うことができます。まず、これらをクラスにインポートします。

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

次に、このメソッドを追加します。このメソッドは、低レベルのメソッドを使用してプロパティ名を取得します。

- (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];
    }
}

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

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

  2. これは、合成されたプロパティでのみ機能し、ivarでは機能しません。

エンコードする前にencodeWithCoderの値のタイプをチェックするか、setValueForUndefinedKeyメソッドをオーバーライドして問題をより適切に処理することにより、エラー処理を追加できます。

アップデート:

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

更新2:

継承された読み取り専用のプロパティを正しく処理するように回答を更新しました。

于 2012-01-20T07:31:16.727 に答える
0

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

https://github.com/flexme/DYCodingを確認してください。コンパイル済みのコードと同じくらい高速で、さまざまなプロパティ タイプをサポートしています。

于 2016-07-12T04:38:23.903 に答える