Key-Value-Observing a to-many relationship in Cocoa

谁都会走 提交于 2019-12-28 03:38:11

问题


I am trying to get key-value-observing to work for an NSMutableArray. Below is the .h file for MyObservee, the observed class:

@interface MyObservee : NSObject {
    @private int someValue;
    @private NSMutableArray *someArray;
}

@property (readwrite,assign) int someValue;
- (NSMutableArray *)someArray;
@end

The class MyObserver implements observeValueForKeyPath:ofObject:change:context:. Here is how I add the observer:

MyObservee *moe = [[MyObservee alloc] init];
MyObserver *mobs = [[MyObserver alloc] init];

[moe addObserver:mobs 
      forKeyPath:@"someArray" 
         options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) 
         context:NULL];

[moe.someArray addObject:@"hi there"];

How come the addObject: message isn't triggering as a change to the someArray key path? I have a feeling there's something I don't fully understand here.


回答1:


You need to implement the indexed array accessors as defined in the KVC programming guide. Then you must use those accessors to access the array and the KVO triggering will work. You can also call -mutableArrayValueForKey: and use that array to addObject: and such and it will in turn call the accessor methods and the KVO triggering will occur as well. There are also set accessors for use in for NSSets, see here and here.

Example:

@interface MyClass : NSObject
{
    NSMutableArray *_orders;
}

@property(retain) NSMutableArray *orders;

- (NSUInteger)countOfOrders;
- (id)objectInOrdersAtIndex:(NSUInteger)index;
- (void)insertObject:(id)obj inOrdersAtIndex:(NSUInteger)index;
- (void)removeObjectFromOrdersAtIndex:(NSUInteger)index;
- (void)replaceObjectInOrdersAtIndex:(NSUInteger)index withObject:(id)obj;


@end



回答2:


Unfortunately, The NSArray classes are note KVO compliant. They are KVC compliant, but you can't observe them directly like you're trying to do here. The easiest way to get this functionality would be to use an NSArrayController. The NSArray controller is KVO compliant and will alert you when items are added or removed. In your example, your observer would be notified if you actually changed the array itself. For instance, if you did something like this:

[moe setSomeArray:[NSMutableArray array]];

Which is probably not what you wanted at all :) Just as an aside, NSDictionary is actually KVO compliant so you could use that, if you chose. Or you could write a wrapper subclass of NSMutableArray that just creates a real mutable array as its backing store but just forwards on all messages to it except addObject and removeObject which you could override to trigger notifications.




回答3:


Why are you passing your private array to another object? It's not so private when you let other objects handle it.

As s-bug said, you should implement the accessors and use mutableArrayValueForKey: to mutate the property. I add that you should not be exposing that private array at all—your someArray method should return an immutable copy of the array.

Furthermore, I call your attention to Jason Coco's comment on s-bug's answer. To paraphrase him, you should probably use an NSArrayController as an additional step of separation between myObservee and myObserver. This is a very good suggestion, and if you don't have a specific reason to directly observe the property, you should take it. (Among the benefits is that you can then use Bindings to connect views to the new array controller.)



来源:https://stackoverflow.com/questions/477204/key-value-observing-a-to-many-relationship-in-cocoa

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!