2010-06-04 25 views
65

Tôi cần phải viết một phương thức setter tùy chỉnh cho một trường (chúng tôi sẽ gọi nó là foo) trong lớp con của tôi là NSManagedObject. foo được xác định trong mô hình dữ liệu và Xcode đã tự động phát hiện các trường @property@dynamic trong các tệp .h và .m tương ứng.Phương thức setter tùy chỉnh trong Core-Data

Nếu tôi viết setter của tôi như thế này:

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

sau đó tôi nhận được một cảnh báo trình biên dịch trên các cuộc gọi đến super.

Ngoài ra, nếu tôi làm điều này:

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

sau đó tôi kết thúc trong một vòng lặp vô hạn.

Vậy cách tiếp cận chính xác để viết setter tùy chỉnh cho lớp con của NSManagedObject là gì?

Trả lời

1

Đây là cách của Apple để ghi đè thuộc tính NSManagedObject (không phá vỡ KVO), trong của bạn.m:

@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 là một built-in diệu phương pháp bạn chỉ cần thêm các định nghĩa cho. Như đã thấy ở dưới cùng của trang này What's New in Core Data in macOS 10.12, iOS 10.0, tvOS 10.0, and watchOS 3.0

+0

Điểm tốt @malhal - Tôi không biết sự thay đổi này trong iOS 10. –

100

Theo the documentation, nó muốn được:

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

Đây là, tất nhiên, bất chấp sự thật rằng NSManagedObjects chỉ muốn NSNumbers, NSDates, NSDatas, và NSStrings như các thuộc tính.

Tuy nhiên, đây có thể không phải là cách tiếp cận tốt nhất. Vì bạn muốn điều gì đó xảy ra khi giá trị của các thay đổi bất động sản foo của bạn, tại sao không chỉ quan sát nó với Key Value Observing? Trong trường hợp này, nó có vẻ như "KVO là con đường để đi".

+0

Cảm ơn Dave. Xin lỗi trường này thực sự được định nghĩa là một 'NSNumber *' nhưng tôi đã cố gắng khái quát vấn đề. Tôi đã thử những gì bạn đã đề xuất ở trên, nhưng tôi nhận được cảnh báo trình biên dịch rằng lớp của tôi có thể không phản hồi lại '-setPrimitivePositionX:'. Bất kỳ ý tưởng? Ý tưởng hay. KVO. Đâu là nơi tốt nhất để đăng ký? Trong '- (void) awakeFromInsert'? Tôi muốn hủy đăng ký trong '- (void) dealloc' phải không? –

+0

OK, tôi đã thêm một phần '@ giao diện' riêng tư trong tệp .m và đã khắc phục cảnh báo, nhưng các mã vẫn không hoạt động như mong đợi. Tôi cần phải gỡ lỗi này! –

+0

Khi điều tra thêm, setter được gọi chính xác khi tôi đặt giá trị một cách rõ ràng trên đối tượng, nhưng nó không được gọi khi tôi sử dụng NSUndoManager để hoàn nguyên thay đổi. Trong trường hợp đó tôi đoán KVO là một cách tiếp cận toàn diện tốt hơn. –

17

Tôi nghĩ rằng đó là một sai lầm nhỏ: sử dụng

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

thay vì

[self setPrimitiveFoo:inFoo]; 

này làm việc cho tôi.

+0

Cảm ơn Martin. Như bạn nói, KVO là con đường để đi (Tôi đang đăng ký trong '- (void) awakeFromFetch' và unregistering trong' - (void) dealloc' và tôi đã thực hiện điều này và nó hoạt động với undo –

+7

không sử dụng - (void) dealloc để unregister, unregister quan sát trong - (void) willTurnIntoFault thay vào đó.Nếu không, bạn sẽ nhận được thông báo không cần thiết khi một đối tượng ist biến thành một lỗi Các đối tượng mới chèn vào không nhận được một - (void) awakeFromFetch tin nhắn sử dụng - (void) awakeFromInsert quá –

+1

@Andrew Ebling, hãy trả lời câu hỏi của riêng bạn và bao gồm mã nguồn của giải pháp của bạn. (Hãy thay đổi tên biến, v.v., nhưng hãy giữ nó tốt.) Tôi đang làm việc Tôi đang tìm ra điều này bằng cách đọc liên kết trên KVC, nhưng nhìn thấy giải pháp của bạn sẽ rất hữu ích! :) – ma11hew28

19

Đây là cách tôi đang thực hiện KVO trên thuộc tính id của Photo : NSManagedObject. Nếu ID của ảnh thay đổi, hãy tải xuống ảnh mới.

#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

Nếu một đối tượng bị xóa, bối cảnh được lưu (đối tượng actu ally deallocated), undo được gọi, việc quan sát sẽ bị thiếu. Trong 10.6+ bạn cũng có thể thiết lập quan sát trong awakeFromSnapshotEvents. Đối với khả năng tương thích ngược hãy xem https://github.com/mbrugger/CoreDataDependentProperties Nó giải quyết chính xác tất cả những vấn đề này. –

+2

Từ tài liệu của apple, bạn nên gọi siêu trên "awakeFromFetch" và "awakeFromInsert" – Fervus

+0

Bài kiểm tra [newValue isEqualToString: oldValue] là không cần thiết vì thông báo sẽ chỉ kích hoạt nếu chúng không giống nhau. –

0

Đây là cách bạn làm điều đó 1-n (và tôi đoán n-m) mối quan hệ:

Cho phép giả định tên mối quan hệ được gọi là "sinh viên" trong một đối tượng được gọi là "Trường".

Trước tiên, bạn cần phải xác định các phương thức truy nhập nguyên thủy cho NSMutableSet. Xcode sẽ không tự động tạo các mã này cho bạn.

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

Tiếp theo bạn có thể xác định phương thức truy cập của mình. Ở đây tôi sẽ ghi đè lên 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]; 
} 
Các vấn đề liên quan