web-dev-qa-db-ja.com

Core-Dataのカスタムセッターメソッド

fooのサブクラスで、フィールドのカスタムセッターメソッドを記述する必要があります(NSManagedObjectと呼びます)。 fooはデータモデルで定義されており、Xcodeは.hおよび.mファイルにそれぞれ自動生成された@propertyおよび@dynamicフィールドを持っています。

このようにセッターを書くと:

- (void)setFoo: (NSObject *)inFoo {
    [super setFoo: inFoo];
    [self updateStuff];
}

superの呼び出しでコンパイラの警告が表示されます。

または、これを行う場合:

- (void)setFoo: (NSObject *)inFoo {
    [super setValue: inFoo forKey: inFoo];
    [self updateStuff];
}

その後、無限ループに陥ります。

NSManagedObjectのサブクラスのカスタムセッターを作成する正しいアプローチは何ですか?

68
Andrew Ebling

.mで、NSManagedObjectプロパティを(KVOを壊さずに)オーバーライドするAppleの方法):

@interface Transaction (DynamicAccessors)
- (void)managedObjectOriginal_setDate:(NSDate *)date;
@end

@implementation Transaction
@dynamic date;

- (void)setDate:(NSDate *)date
{
    // invoke the dynamic implementation of setDate (calls the willChange/didChange for you)
    [self managedObjectOriginal_setDate:(NSString *)date;

    // your custom code
}

managedObjectOriginal_propertyNameは組み込みのmagicメソッドで、定義を追加するだけです。このページの下部にあるように macOS 10.12、iOS 10.0、tvOS 10.0、およびwatchOS 3.0のコアデータの新機能

7
malhal

ドキュメント によると、次のようになります。

- (void) setFoo:(NSObject *)inFoo {
  [self willChangeValueForKey:@"foo"];
  [self setPrimitiveValue:inFoo forKey:@"foo"];
  [self didChangeValueForKey:@"foo"];
}

もちろん、これはNSManagedObjectsが属性としてNSNumbersNSDatesNSDatas、およびNSStringsのみを必要とするという事実を無視します。

ただし、これは最善のアプローチではない場合があります。 fooプロパティの値が変更されたときに何かが発生するようにしたいので、なぜ Key Value Observing でそれを観察しないのですか?この場合、「KVOが道を行く」ように聞こえます。

102
Dave DeLong

Photo : NSManagedObjectid属性でKVOを実行する方法は次のとおりです。写真のIDが変更された場合は、新しい写真をダウンロードします。

#pragma mark NSManagedObject

- (void)awakeFromInsert {
    [self observePhotoId];
}

- (void)awakeFromFetch {
    [self observePhotoId];
}

- (void)observePhotoId {
    [self addObserver:self forKeyPath:@"id"
              options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:NULL];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change
                       context:(void *)context {
    if ([keyPath isEqualToString:@"id"]) {
        NSString *oldValue = [change objectForKey:NSKeyValueChangeOldKey];
        NSString *newValue = [change objectForKey:NSKeyValueChangeNewKey];        
        if (![newValue isEqualToString:oldValue]) {
            [self handleIdChange];
        }
    }
}

- (void)willTurnIntoFault {
    [self removeObserver:self forKeyPath:@"id"];
}

#pragma mark Photo

- (void)handleIdChange {
    // Implemented by subclasses, but defined here to hide warnings.
    // [self download]; // example implementation
}
19
ma11hew28

私はわずかな間違いがあると思う:使用

 [self setPrimitiveValue:inFoo forKey:@"foo"];

の代わりに

 [self setPrimitiveFoo:inFoo];

これは私のために働く。

17
Martin Brugger

1-n(およびn-mと仮定)の関係を行う方法は次のとおりです。

関係名が「学校」と呼ばれるオブジェクトの「学生」と呼ばれると仮定しましょう。

最初に、NSMutableSetのプリミティブアクセサーメソッドを定義する必要があります。 Xcodeはこれらを自動的に生成しません。

@interface School(PrimitiveAccessors)
- (NSMutableSet *)primitiveStudents;
@end

次に、アクセサメソッドを定義できます。ここで、セッターをオーバーライドします。

- (void)addStudentsObject:(Student *)student
{
  NSSet *changedObjects = [[NSSet alloc] initWithObjects:&student count:1];

  [self willChangeValueForKey:@"students"
              withSetMutation:NSKeyValueUnionSetMutation
                 usingObjects:changedObjects];

  [[self primitiveStudents] addObject:value];

  [self didChangeValueForKey:@"students"
             withSetMutation:NSKeyValueUnionSetMutation
                usingObjects:changedObjects];
}
0
David