2010-06-04 55 views
65

我需要爲我的子類NSManagedObject中的字段(我們將其稱爲foo)編寫自定義設置方法。 foo在數據模型中定義,並且Xcode分別在.h和.m文件中自動生成@property@dynamic字段。核心數據中的自定義設置方法

如果我寫我的setter方法是這樣的:

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

然後我上調用super編譯器警告。

或者,如果我這樣做:

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

然後我在一個無限循環結束。

那麼,爲NSManagedObject的子類編寫自定義setter的正確方法是什麼?

回答

1

這裏是蘋果的方式來重寫NSManagedObject屬性(不破壞KVO),在你的。L:

@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是一個內置魔術方法,你只需要添加的定義。正如本頁底部所示What's New in Core Data in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0

+0

好點@malhal - 我沒有意識到iOS 10中的這種變化。 – 2018-02-26 11:09:03

100

the documentation,它會是:

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

這是當然的,忽略的事實是NSManagedObjects只想NSNumbersNSDatesNSDatasNSStrings爲屬性。

但是,這可能不是最好的方法。既然你想在foo屬性的值發生變化時發生某些事情,爲什麼不用Key Value Observing來觀察呢?在這種情況下,這聽起來像「KVO的路要走」。

+0

謝謝戴夫。道歉這個字段實際上被定義爲一個'NSNumber *',但我試圖推廣這個問題。 我嘗試了上面的建議,但是我得到一個編譯器警告,說我的類可能不會響應'-setPrimitivePositionX:'。有任何想法嗎? 好主意重新。志願。哪裏會是最好的註冊地點?在' - (void)awakeFromInsert'中?我會在' - (void)dealloc'註冊? – 2010-06-04 06:24:17

+0

好的,我在.m文件中添加了一個專用的@ @接口部分,並修復了警告,但代碼仍然不像預期的那樣運行。我需要調試這個! – 2010-06-04 06:28:59

+0

在進一步調查中,當我明確設置對象上的值時,setter被正確調用,但當我使用NSUndoManager恢復更改時,它不會被調用。在這種情況下,我猜KVO是一種更好的全面方法。 – 2010-06-04 06:32:07

17

我覺得有一個小錯誤: 使用

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

代替

[self setPrimitiveFoo:inFoo]; 

這對我的作品。

+0

謝謝馬丁。正如你所說,KVO是要走的路(我在' - (void)awakeFromFetch'中註冊並在' - (void)dealloc'中取消註冊,現在我已經實現了這個功能,並且它可以與undo一起使用。 – 2010-06-04 06:54:56

+7

不使用 - (void)dealloc取消註冊,在 - (void)willTurnIntoFault中取消註冊觀察值,否則當對象變爲錯誤時會收到不必要的通知。 插入的新對象沒有得到 - (void)awakeFromFetch消息。 使用 - (void)awakeFromInsert太 – 2010-06-04 07:14:06

+1

@Andrew Ebling,請回答你自己的問題,幷包括你的解決方案的源代碼。(隨意更改變量名等,但請保持良好。)我正在努力通過閱讀KVC上的鏈接來搞清楚,但看到你的解決方案會非常有幫助!:) – ma11hew28 2011-04-26 04:11:48

19

以下是我如何在id屬性Photo : NSManagedObject上做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 
} 
+0

如果一個對象被刪除,上下文被保存(對象激活盟友解除分配),撤消調用,觀察將會丟失。在10.6+中,您還可以在awakeFromSnapshotEvents中建立觀察。爲了向後兼容,請查看https://github.com/mbrugger/CoreDataDependentProperties它完全解決了所有這些問題。 – 2011-04-26 17:02:50

+2

從apple的文檔中,你應該調用超級「awakeFromFetch」和「awakeFromInsert」 – Fervus 2014-01-21 08:58:02

+0

[newValue isEqualToString:oldValue]測試是不必要的,因爲通知只會在它們不相同時觸發。 – 2017-06-12 13:16:20

0

這裏是你如何做到這一點1-N(和我相信N-M)的關係:

讓我們假設的關係,名字叫做名爲「學校」的對象「學生」。

首先,您需要爲NSMutableSet定義原始存取器方法。 Xcode不會爲你自動生成。

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

接下來,您可以定義您的存取方法。這裏我要重寫setter。

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