On Objective-C/Cocoa Key-Value coding and arrays

大憨熊 提交于 2020-01-13 05:47:53

问题


I'm trying to work out the "correct" way to handle populating an array with key-value coding for an iPhone app. I've come up with something that works, but it's fairly hackish. Basically I'm parsing an XML document into a set of code-generated models. Let's assume the XML is of this format:

<foo>
    <bar>
        <item name="baz" />
        <item name="bog" />
    </bar>
</foo>

On my generated object that represents the Bar element, I have an NSMutableArray defined for the sub-node:

@interface Bar : NSObject {
    NSMutableArray *item;
}
@end

So by default when I call setValue:forKey: on an instance of Bar, it ends up overwriting the instance of NSMutableArray with a single instance of the Item object. What I've currently done to get this working is where it gets hacky. I renamed the array instance variable to be something else, let's say the plural form of the name:

@interface Bar : NSObject {
    NSMutableArray *items;
}
@end

This causes the default accessor for setValue:forKey: to miss. I then added this method to the Bar implementation:

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    if([key isEqualToString:@"item"]) {
        if(items == nil) {
            items = [[NSMutableArray alloc] init];
            [items retain];         
        }
        [items addObject:value];
    }
}

And everything works! I'm sure there must be a better way to do this though! I've read through the Key-Value Coding Programming Guide, but I must be missing something, because I'm unclear as to how things are supposed to work for array accessors. I've tried implementing countOf: and objectInAtIndex: as they KVC Programming guide seems to indicate, but that still results with my NSMutableArray being overwritten with an instance of the Item type.


回答1:


If you want to use KVC properties for an array you should look into the documentation for mutableArrayValueForKey:

Basically with your class:

@interface Bar : NSObject {
    NSMutableArray *items;
}
@end

You would define these methods as a minimum, assuming you initialized your array elsewhere:

- (void)insertObject:(id)object inItemsAtIndex:(NSUInteger)index {
    [items insertObject:object atIndex:index];
}

- (void)removeObjectFromItemsAtIndex:(NSUInteger)index {
    [items removeObjectAtIndex:index];
}

- (NSArray *)items {
    /* depending on how pedantic you want to be
       possibly return [NSArray arrayWithArray:items] */
    return items;
}

Once you've implemented those methods you can call [bar mutableArrayValueForKey:@"items"]. It will return an NSMutableArray proxy object that you can add and remove objects from. That object will in turn generate the appropriate KVO messages and uses the methods you just defined to interact with your actual array.




回答2:


As Martin suggests, you cannot directly observe an array to see when items are added or removed. Instead, this would be considered a "to-many" attribute of your Bar object, and you would have to deal with making the KVO assertions yourself. You can do something like this for adding and removing items in bar:

@interface Bar : NSObject
{
    NSMutableArray *items;
}
-(void)addItem:(id)item;
-(void)removeItem:(id)item;
@end

@implementation Bar
-(id)init
{
    if( (self = [super init]) ) {
        items = [[NSMutableArray alloc] init];
    }
    return self;
}

-(void)addItem:(id)item
{
    NSParameterAssert(item);
    NSIndexSet *iset = [NSIndexSet indexSetWithIndex:[items count]];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:iset forKey:@"items"];
    [items addObject:item];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:iset forKey:@"items"];
}

-(void)removeItem:(id)item
{
    NSParameterAssert(item);
    NSIndexSet *iset = [NSIndexSet indexSetWithIndex:[items indexForObject:item]];
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:iset forKey:@"items"];
    [items removeObject:item];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:iset forKey:@"items"];
}
@end

If you wanted to be able to set/replace items directly, you would have to come up with some way to do that yourself. Normally, I would suggest and NSArrayController for this kind of thing, but since you're using the iPhone, you will have to basically create this functionality yourself.




回答3:


Well, there's 2 ways you can go about it:

  1. Add an "Add Item" method to your Bar class where you pass in the item
  2. Make sure you're passing in the whole array of items when doing KVC

I don't believe there is a way to add an item to an array using KVC.




回答4:


It is sufficient to call valueForKey:@"item" and the result will be mutable. So you can say:

NSMutableArray* m = [theBar valueForKey:@"item"];
[item addObject: value];

You don't need those other methods unless you're implementing a facade (a key for which there is not mutable instance variable).



来源:https://stackoverflow.com/questions/494085/on-objective-c-cocoa-key-value-coding-and-arrays

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