indexed accessor properties and NSArray/NSMutableArray's

  • I've now read the KVC documentation quite a number of times ..
    especially with regards to indexed accessor properties trying to
    really understand them. After a bit of messing about in code this is
    my understanding ..

    1) if your property is an array you don't need to supply the
    countOf<key> and objectIn<key>AtIndex: methods for reading the Array,
    it will use the Array directly. You can supply them if you want in
    which case it will use them.

    2) If you don't supply the mutable access methods
    insertObject:in<key>AtIndex: and removeObjectFrom<key>AtIndex: but
    your property is a NSArray, it will still actually work, what will
    happen is the entire array property will be set each time you mutate
    it using KVC or bindings to a brand new array, not very efficient.

    3) 2) above is true not only for an NSArray, (obviously as it's not
    mutable) but for an NSMutableArray too. Even if your property is a
    NSMutableArray which could be mutated, if you haven't supplied the
    mutable access methods, it won't be, the entire array will be replaced
    each time. (this surprised me). If you want the efficient insert/
    remove for your NSMutableArray property, you have to write the
    accessor methods explicitly.

    1) didn't suprise me as the documentation indicates you only need the
    accessor methods when the property isn't an array.
    2) surprised me a bit as the documentation states that to have mutable
    access you *must* implement two extra methods. However if your entire
    property is an NSArray or subclass thereof, you don't have to
    implement them, it just does it rather slowly.
    3) did surprise me a bit given that 2) worked, I expected if it was
    smart enough to work around the absence of the mutator methods in the
    case of an NSArray, it would realize the property was a MutableArray,
    and mutate it directly, but no, it does it the slow way.

    I could imagine not bothering to write the mutator methods just seeing
    that it worked automagically and never realising that the entire Array
    is being swapped out each time I changed it, probably not very
    efficient. So the moral of the story for me is .. if I want any multi-
    value for a key, even if the underlying instance variable is an array,
    write the accessor/mutator methods every time.
  • The basic problem is, that KVO needs Notification-Messages. If you use
    KVC-compliant accessor methods this will be done for you by a "faked
    class".

    If you have a mutable array and change the content (removal, insertion
    …), there is no chance of the KVO-runtime system to get a notification
    about this. Since you do not use the official methods, nobody does
    this work for you. So the work is undone: No snchronization.

    You can (and in this case: have to) trigger the notifications
    manually. Something like this:
    // typed in Mail.app, no warranty for nothing:
    NSMutableArray* myRefs = self.myRefs;
    [self willChange:NSKeyValueChangeInsertion valuesAtIndex:[NSIndexSet
    indexSetWithIndex:0] withKey:@"myRefs"];
    [myRefs insertObject:… atIndex:0];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndex:[NSIndexSet
    indexSetWithIndex:0] withKey:@"myRefs"];

    Amin

    Am Di,05.08.2008 um 17:22 schrieb Roland King:

    > I've now read the KVC documentation quite a number of times ..
    > especially with regards to indexed accessor properties trying to
    > really understand them. After a bit of messing about in code this is
    > my understanding ..
    >
    > 1) if your property is an array you don't need to supply the
    > countOf<key> and objectIn<key>AtIndex: methods for reading the
    > Array, it will use the Array directly. You can supply them if you
    > want in which case it will use them.
    >
    > 2) If you don't supply the mutable access methods
    > insertObject:in<key>AtIndex: and removeObjectFrom<key>AtIndex: but
    > your property is a NSArray, it will still actually work, what will
    > happen is the entire array property will be set each time you mutate
    > it using KVC or bindings to a brand new array, not very efficient.
    >
    > 3) 2) above is true not only for an NSArray, (obviously as it's not
    > mutable) but for an NSMutableArray too. Even if your property is a
    > NSMutableArray which could be mutated, if you haven't supplied the
    > mutable access methods, it won't be, the entire array will be
    > replaced each time. (this surprised me). If you want the efficient
    > insert/remove for your NSMutableArray property, you have to write
    > the accessor methods explicitly.
    >
    >
    > 1) didn't suprise me as the documentation indicates you only need
    > the accessor methods when the property isn't an array.
    > 2) surprised me a bit as the documentation states that to have
    > mutable access you *must* implement two extra methods. However if
    > your entire property is an NSArray or subclass thereof, you don't
    > have to implement them, it just does it rather slowly.
    > 3) did surprise me a bit given that 2) worked, I expected if it was
    > smart enough to work around the absence of the mutator methods in
    > the case of an NSArray, it would realize the property was a
    > MutableArray, and mutate it directly, but no, it does it the slow way.
    >
    > I could imagine not bothering to write the mutator methods just
    > seeing that it worked automagically and never realising that the
    > entire Array is being swapped out each time I changed it, probably
    > not very efficient. So the moral of the story for me is .. if I want
    > any multi-value for a key, even if the underlying instance variable
    > is an array, write the accessor/mutator methods every time.

    Amin Negm-Awad
    <negm-awad...>
  • On Aug 5, 2008, at 10:22 AM, Roland King wrote:

    > I've now read the KVC documentation quite a number of times ..
    > especially with regards to indexed accessor properties trying to
    > really understand them. After a bit of messing about in code this
    > is my understanding ..
    >
    > 1) if your property is an array you don't need to supply the
    > countOf<key> and objectIn<key>AtIndex: methods for reading the
    > Array, it will use the Array directly. You can supply them if you
    > want in which case it will use them.
    >
    > 2) If you don't supply the mutable access methods
    > insertObject:in<key>AtIndex: and removeObjectFrom<key>AtIndex: but
    > your property is a NSArray, it will still actually work, what will
    > happen is the entire array property will be set each time you
    > mutate it using KVC or bindings to a brand new array, not very
    > efficient.
    >
    > 3) 2) above is true not only for an NSArray, (obviously as it's not
    > mutable) but for an NSMutableArray too. Even if your property is a
    > NSMutableArray which could be mutated, if you haven't supplied the
    > mutable access methods, it won't be, the entire array will be
    > replaced each time. (this surprised me). If you want the efficient
    > insert/remove for your NSMutableArray property, you have to write
    > the accessor methods explicitly.
    >
    >
    > 1) didn't suprise me as the documentation indicates you only need
    > the accessor methods when the property isn't an array.
    > 2) surprised me a bit as the documentation states that to have
    > mutable access you *must* implement two extra methods. However if
    > your entire property is an NSArray or subclass thereof, you don't
    > have to implement them, it just does it rather slowly.
    > 3) did surprise me a bit given that 2) worked, I expected if it was
    > smart enough to work around the absence of the mutator methods in
    > the case of an NSArray, it would realize the property was a
    > MutableArray, and mutate it directly, but no, it does it the slow way.
    >
    > I could imagine not bothering to write the mutator methods just
    > seeing that it worked automagically and never realising that the
    > entire Array is being swapped out each time I changed it, probably
    > not very efficient. So the moral of the story for me is .. if I
    > want any multi-value for a key, even if the underlying instance
    > variable is an array, write the accessor/mutator methods every time.

    Roland,

    How are you mutating your array? If you do [[myObject
    mutableArrayValueForKey:@"myArray"] addObject:anObject], it should
    preserve the current NSMutableArray even without indexed accessors.
    This seems to work in my testing, at least.

    -Jeff
  • On Aug 5, 2008, at 11:50 PM, Jeff Johnson wrote:

    > On Aug 5, 2008, at 10:22 AM, Roland King wrote:
    >
    >> I've now read the KVC documentation quite a number of times ..
    >> especially with regards to indexed accessor properties trying to
    >> really understand them. After a bit of messing about in code this
    >> is my understanding ..
    >>
    >> 1) if your property is an array you don't need to supply the
    >> countOf<key> and objectIn<key>AtIndex: methods for reading the
    >> Array, it will use the Array directly. You can supply them if you
    >> want in which case it will use them.
    >>
    >> 2) If you don't supply the mutable access methods
    >> insertObject:in<key>AtIndex: and removeObjectFrom<key>AtIndex: but
    >> your property is a NSArray, it will still actually work, what will
    >> happen is the entire array property will be set each time you
    >> mutate it using KVC or bindings to a brand new array, not very
    >> efficient.
    >>
    >> 3) 2) above is true not only for an NSArray, (obviously as it's not
    >> mutable) but for an NSMutableArray too. Even if your property is a
    >> NSMutableArray which could be mutated, if you haven't supplied the
    >> mutable access methods, it won't be, the entire array will be
    >> replaced each time. (this surprised me). If you want the efficient
    >> insert/remove for your NSMutableArray property, you have to write
    >> the accessor methods explicitly.
    >>
    >>
    >> 1) didn't suprise me as the documentation indicates you only need
    >> the accessor methods when the property isn't an array.
    >> 2) surprised me a bit as the documentation states that to have
    >> mutable access you *must* implement two extra methods. However if
    >> your entire property is an NSArray or subclass thereof, you don't
    >> have to implement them, it just does it rather slowly.
    >> 3) did surprise me a bit given that 2) worked, I expected if it was
    >> smart enough to work around the absence of the mutator methods in
    >> the case of an NSArray, it would realize the property was a
    >> MutableArray, and mutate it directly, but no, it does it the slow
    >> way.
    >>
    >> I could imagine not bothering to write the mutator methods just
    >> seeing that it worked automagically and never realising that the
    >> entire Array is being swapped out each time I changed it, probably
    >> not very efficient. So the moral of the story for me is .. if I
    >> want any multi-value for a key, even if the underlying instance
    >> variable is an array, write the accessor/mutator methods every time.
    >
    > Roland,
    >
    > How are you mutating your array? If you do [[myObject
    > mutableArrayValueForKey:@"myArray"] addObject:anObject], it should
    > preserve the current NSMutableArray even without indexed accessors.
    > This seems to work in my testing, at least.
    >
    > -Jeff
    >

    An ArrayController is doing it for me. I started with the property
    being an NSArray(), to see what happened, and the entire NSArray() was
    switched on each addition, I know this because I implemented set<key>
    and stuck a breakpoint in it. That made sense. Then I changed it all
    to NSMutableArray, the instance variable, the property accessor
    methods, everything but still it called the set<key> mehtod and
    replaced the entire Array and didn't mutate the one in the property.
    Only after I put in the indexed accessors did it start using them.

    The method you mention is what I was just about to try out in a
    standalone piece of code to check it, it'll be interesting if that
    does mutate the array directly, I did expect however that
    NSMutableArray, being as it's 'the' array controller, would be calling
    the correct KVC-compliant methods. Perhaps it's working differently as
    the ArrayController is bound to the property.
  • Am Di,05.08.2008 um 17:50 schrieb Jeff Johnson:

    > On Aug 5, 2008, at 10:22 AM, Roland King wrote:
    >
    >> I've now read the KVC documentation quite a number of times ..
    >> especially with regards to indexed accessor properties trying to
    >> really understand them. After a bit of messing about in code this
    >> is my understanding ..
    >>
    >> 1) if your property is an array you don't need to supply the
    >> countOf<key> and objectIn<key>AtIndex: methods for reading the
    >> Array, it will use the Array directly. You can supply them if you
    >> want in which case it will use them.
    >>
    >> 2) If you don't supply the mutable access methods
    >> insertObject:in<key>AtIndex: and removeObjectFrom<key>AtIndex: but
    >> your property is a NSArray, it will still actually work, what will
    >> happen is the entire array property will be set each time you
    >> mutate it using KVC or bindings to a brand new array, not very
    >> efficient.
    >>
    >> 3) 2) above is true not only for an NSArray, (obviously as it's not
    >> mutable) but for an NSMutableArray too. Even if your property is a
    >> NSMutableArray which could be mutated, if you haven't supplied the
    >> mutable access methods, it won't be, the entire array will be
    >> replaced each time. (this surprised me). If you want the efficient
    >> insert/remove for your NSMutableArray property, you have to write
    >> the accessor methods explicitly.
    >>
    >>
    >> 1) didn't suprise me as the documentation indicates you only need
    >> the accessor methods when the property isn't an array.
    >> 2) surprised me a bit as the documentation states that to have
    >> mutable access you *must* implement two extra methods. However if
    >> your entire property is an NSArray or subclass thereof, you don't
    >> have to implement them, it just does it rather slowly.
    >> 3) did surprise me a bit given that 2) worked, I expected if it was
    >> smart enough to work around the absence of the mutator methods in
    >> the case of an NSArray, it would realize the property was a
    >> MutableArray, and mutate it directly, but no, it does it the slow
    >> way.
    >>
    >> I could imagine not bothering to write the mutator methods just
    >> seeing that it worked automagically and never realising that the
    >> entire Array is being swapped out each time I changed it, probably
    >> not very efficient. So the moral of the story for me is .. if I
    >> want any multi-value for a key, even if the underlying instance
    >> variable is an array, write the accessor/mutator methods every time.
    >
    > Roland,
    >
    > How are you mutating your array? If you do [[myObject
    > mutableArrayValueForKey:@"myArray"] addObject:anObject], it should
    > preserve the current NSMutableArray even without indexed accessors.
    > This seems to work in my testing, at least.
    Yup, that works, because the methods -mutableArrayValueForKey:, -
    mutableArrayValueForKeyPath:, -mutableSetValueForKey: and -
    mutableSetValueForKeyPath: manually triggers the notifications.

    But:

    1. I thought, that it is a "miracle" to him, why only the specified
    patterns work, but no other.

    2.1. If -mutableCollectionValueForKey: should work efficient, you have
    to return a mutable ivar on your accessors (getter). This means, that
    *everyone* can change items in that collection without triggering kvo.
    This is the opposition of encapsulation and very, very dangerous.

    2.2. It is possible to give the accessors an immutable type (NSArray,
    NSSet) and "in reality" return a mutable instacne. IIRC, -
    mutableCollectionValueForKey: will check the returned class of the
    instance, not for the type of the arguments. "(I'm nut sure about
    this, but nearly. You can check it in the debugger by looking on the
    instance addresses.)

    Amin

    >
    >
    > -Jeff

    Amin Negm-Awad
    <negm-awad...>
  • On Aug 5, 2008, at 11:50 PM, Jeff Johnson wrote:

    > On Aug 5, 2008, at 10:22 AM, Roland King wrote:
    >
    >> I've now read the KVC documentation quite a number of times ..
    >> especially with regards to indexed accessor properties trying to
    >> really understand them. After a bit of messing about in code this
    >> is my understanding ..
    >>
    >> 1) if your property is an array you don't need to supply the
    >> countOf<key> and objectIn<key>AtIndex: methods for reading the
    >> Array, it will use the Array directly. You can supply them if you
    >> want in which case it will use them.
    >>
    >> 2) If you don't supply the mutable access methods
    >> insertObject:in<key>AtIndex: and removeObjectFrom<key>AtIndex: but
    >> your property is a NSArray, it will still actually work, what will
    >> happen is the entire array property will be set each time you
    >> mutate it using KVC or bindings to a brand new array, not very
    >> efficient.
    >>
    >> 3) 2) above is true not only for an NSArray, (obviously as it's not
    >> mutable) but for an NSMutableArray too. Even if your property is a
    >> NSMutableArray which could be mutated, if you haven't supplied the
    >> mutable access methods, it won't be, the entire array will be
    >> replaced each time. (this surprised me). If you want the efficient
    >> insert/remove for your NSMutableArray property, you have to write
    >> the accessor methods explicitly.
    >>
    >>
    >> 1) didn't suprise me as the documentation indicates you only need
    >> the accessor methods when the property isn't an array.
    >> 2) surprised me a bit as the documentation states that to have
    >> mutable access you *must* implement two extra methods. However if
    >> your entire property is an NSArray or subclass thereof, you don't
    >> have to implement them, it just does it rather slowly.
    >> 3) did surprise me a bit given that 2) worked, I expected if it was
    >> smart enough to work around the absence of the mutator methods in
    >> the case of an NSArray, it would realize the property was a
    >> MutableArray, and mutate it directly, but no, it does it the slow
    >> way.
    >>
    >> I could imagine not bothering to write the mutator methods just
    >> seeing that it worked automagically and never realising that the
    >> entire Array is being swapped out each time I changed it, probably
    >> not very efficient. So the moral of the story for me is .. if I
    >> want any multi-value for a key, even if the underlying instance
    >> variable is an array, write the accessor/mutator methods every time.
    >
    > Roland,
    >
    > How are you mutating your array? If you do [[myObject
    > mutableArrayValueForKey:@"myArray"] addObject:anObject], it should
    > preserve the current NSMutableArray even without indexed accessors.
    > This seems to work in my testing, at least.
    >
    > -Jeff
    >

    I don't get that in my testing. As trivial an example as I can make
    below, Foo, one property, prop, it's a NSMutableArray*, has a getter
    and a setter. The main() function calls

    [ [ foo mutableArrayValueForKey:@"prop" ] addObject:@"something" ]

    and that replaces the array by calling setProp:, doesn't mutate the
    one there.

    Only when I add both (not just the insert, but both insert and remove)
    of the mutator methods, will access via the mutableArrayForKey call
    insertObject:inPropAtIndex:

    @interface Foo : NSObject {
    NSMutableArray *prop;
    }

    -(id)init;
    -(void)dealloc;

    -(NSMutableArray*)prop;
    -(void)setProp:(NSMutableArray*)propVal;

    @end

    @implementation Foo

    -(id)init
    {
    self = [ super init ];
    if( self )
    {
      prop = [ [ NSMutableArray alloc ] init ];
    }

    return self;
    }
    -(void)dealloc
    {
    [ prop dealloc ];
    [ super dealloc ];
    }

    -(NSMutableArray*)prop
    {
    return prop;
    }
    -(void)setProp:(NSMutableArray*)propVal
    {
    [ propVal retain ];
    [ prop release ];
    prop = propVal;
    }

    @end

    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

        Foo *foo = [ [ Foo alloc ] init ];

        [[ foo mutableArrayValueForKey:@"prop" ] addObject:@"Another
    String" ];

        [pool drain];
        return 0;
    }
  • On Aug 5, 2008, at 11:56 AM, Roland King wrote:

    > On Aug 5, 2008, at 11:50 PM, Jeff Johnson wrote:
    >
    >> Roland,
    >>
    >> How are you mutating your array? If you do [[myObject
    >> mutableArrayValueForKey:@"myArray"] addObject:anObject], it should
    >> preserve the current NSMutableArray even without indexed
    >> accessors. This seems to work in my testing, at least.
    >>
    >> -Jeff
    >>
    >
    > I don't get that in my testing. As trivial an example as I can make
    > below, Foo, one property, prop, it's a NSMutableArray*, has a
    > getter and a setter. The main() function calls
    >
    > [ [ foo mutableArrayValueForKey:@"prop" ] addObject:@"something" ]
    >
    > and that replaces the array by calling setProp:, doesn't mutate the
    > one there.
    >
    > Only when I add both (not just the insert, but both insert and
    > remove) of the mutator methods, will access via the
    > mutableArrayForKey call insertObject:inPropAtIndex:
    >
    >
    >
    > @interface Foo : NSObject {
    > NSMutableArray *prop;
    > }
    >
    > -(id)init;
    > -(void)dealloc;
    >
    > -(NSMutableArray*)prop;
    > -(void)setProp:(NSMutableArray*)propVal;
    >
    > @end
    >
    >
    >
    > @implementation Foo
    >
    > -(id)init
    > {
    > self = [ super init ];
    > if( self )
    > {
    > prop = [ [ NSMutableArray alloc ] init ];
    > }
    >
    > return self;
    > }
    > -(void)dealloc
    > {
    > [ prop dealloc ];
    > [ super dealloc ];
    > }
    >
    > -(NSMutableArray*)prop
    > {
    > return prop;
    > }
    > -(void)setProp:(NSMutableArray*)propVal
    > {
    > [ propVal retain ];
    > [ prop release ];
    > prop = propVal;
    > }
    >
    > @end
    >
    >
    >
    > int main (int argc, const char * argv[]) {
    > NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    >
    > Foo *foo = [ [ Foo alloc ] init ];
    >
    > [[ foo mutableArrayValueForKey:@"prop" ] addObject:@"Another
    > String" ];
    >
    > [pool drain];
    > return 0;
    > }
    >

    Roland,

    Right, that's because you've defined a setter. See the search
    algorithm documented for mutableArrayValueForKey: at <http://
    developer.apple.com/documentation/Cocoa/Reference/Foundation/
    Protocols/NSKeyValueCoding_Protocol/Reference/Reference.html#//
    apple_ref/doc/uid/20000471-BABJFEAE>. Try removing the setter.

    -Jeff
  • >>
    >> Roland,
    >>
    >> How are you mutating your array? If you do [[myObject
    >> mutableArrayValueForKey:@"myArray"] addObject:anObject], it should
    >> preserve the current NSMutableArray even without indexed accessors.
    >> This seems to work in my testing, at least.
    > Yup, that works, because the methods -mutableArrayValueForKey:, -
    > mutableArrayValueForKeyPath:, -mutableSetValueForKey: and -
    > mutableSetValueForKeyPath: manually triggers the notifications.
    >

    not for me it doesn't, see my other reply, without the mutator methods
    defined in the class, it doesn't preserve the NSMutableArray, it
    changes it for a new array.

    Note none of my mail has been about triggering notifications nor have
    I been talking about using 'behind the scenes' ways of changing
    arrays. My mail has been only about how, when you use a correct KVC
    pattern, a binding or mutableArrayValueForKey: and the property you're
    mutating happens to actually be an Array, it actually ends up getting
    mutated when you do, or do not, provide the mutable accessor methods.

    > But:
    >
    > 1. I thought, that it is a "miracle" to him, why only the specified
    > patterns work, but no other.

    Not so. The documentation says this

    "The key-value coding methods mutableArrayValueForKey: and
    mutableArrayValueForKeyPath: provide mutable access to a to-many
    relationship, regardless of the class used to model the relationship.
    In order to support these methods, your class must implement two
    additional methods for each of these keys: -
    insertObject:in<Key>AtIndex: and -removeObjectFrom<Key>AtIndex:."

    however in the case of an NSArray property, even if you do not
    implement them, mutableArrayValueForKey: still provides mutable access
    to the contents of the array property, albeit by changing the entire
    property for a brand new array. The specified pattern says I *must*
    implement those methods for it to work, however it worked even when I
    didn't, I went to find out why.

    >
    >
    > 2.1. If -mutableCollectionValueForKey: should work efficient, you
    > have to return a mutable ivar on your accessors (getter). This
    > means, that *everyone* can change items in that collection without
    > triggering kvo. This is the opposition of encapsulation and very,
    > very dangerous.
    >

    That I agree with - and my test in the last mail seems to indicate
    that encapsulation is preserved. I wasn't able to get
    mutable<anything> to change the array directly. That's good.
  • On Aug 5, 2008, at 11:01 AM, Negm-Awad Amin wrote:

    > 2.1. If -mutableCollectionValueForKey: should work efficient, you
    > have to return a mutable ivar on your accessors (getter). This
    > means, that *everyone* can change items in that collection without
    > triggering kvo. This is the opposition of encapsulation and very,
    > very dangerous.

    You don't need a getter if you use accessInstanceVariablesDirectly
    (which returns YES by default).

    -Jeff
  • Am Di,05.08.2008 um 19:11 schrieb Roland King:

    >>>
    >>> Roland,
    >>>
    >>> How are you mutating your array? If you do [[myObject
    >>> mutableArrayValueForKey:@"myArray"] addObject:anObject], it should
    >>> preserve the current NSMutableArray even without indexed
    >>> accessors. This seems to work in my testing, at least.
    >> Yup, that works, because the methods -mutableArrayValueForKey:, -
    >> mutableArrayValueForKeyPath:, -mutableSetValueForKey: and -
    >> mutableSetValueForKeyPath: manually triggers the notifications.
    >>
    >
    > not for me it doesn't, see my other reply, without the mutator
    > methods defined in the class, it doesn't preserve the
    > NSMutableArray, it changes it for a new array.
    >
    > Note none of my mail has been about triggering notifications nor
    > have I been talking about using 'behind the scenes' ways of changing
    > arrays. My mail has been only about how, when you use a correct KVC
    > pattern, a binding or mutableArrayValueForKey: and the property
    > you're mutating happens to actually be an Array, it actually ends up
    > getting mutated when you do, or do not, provide the mutable accessor
    > methods.
    Okay, I misunderstood your mail.
    Sorry for sending the answer directly …

    >> But:
    >>
    >> 1. I thought, that it is a "miracle" to him, why only the specified
    >> patterns work, but no other.
    >
    > Not so. The documentation says this
    >
    > "The key-value coding methods mutableArrayValueForKey: and
    > mutableArrayValueForKeyPath: provide mutable access to a to-many
    > relationship, regardless of the class used to model the
    > relationship. In order to support these methods, your class must
    > implement two additional methods for each of these keys: -
    > insertObject:in<Key>AtIndex: and -removeObjectFrom<Key>AtIndex:."
    >
    > however in the case of an NSArray property, even if you do not
    > implement them, mutableArrayValueForKey: still provides mutable
    > access to the contents of the array property, albeit by changing the
    > entire property for a brand new array. The specified pattern says I
    > *must* implement those methods for it to work, however it worked
    > even when I didn't, I went to find out why.
    >
    No, I think you misunderstood the documentation. It says, that you
    have to implement this methods to get mutable *access*. Mutable access
    means in this context, that "mutating accessor mathods" are used. To
    change the array or build a new array and than sending a setProperty-
    message to the holding instance is not "mutable access", because
    semantically *setting* an (old or new) array is no mutation of an
    existing array.

    Hmm, I know, that I speak englisch very badly. Did you understand,
    what I want to say?

    >>
    >>
    >> 2.1. If -mutableCollectionValueForKey: should work efficient, you
    >> have to return a mutable ivar on your accessors (getter). This
    >> means, that *everyone* can change items in that collection without
    >> triggering kvo. This is the opposition of encapsulation and very,
    >> very dangerous.
    >>
    >
    > That I agree with - and my test in the last mail seems to indicate
    > that encapsulation is preserved. I wasn't able to get
    > mutable<anything> to change the array directly. That's good.
    >

    Amin Negm-Awad
    <negm-awad...>
  • On Aug 5, 2008, at 7:27 PM, Roland King wrote:

    > Thanks. There's always just ONE more piece of documentation to read
    > isn't there. So here's my (possibly) last question. If you define
    > neither the accessor methods nor a getter/setter and make
    > mutableArrayValueByKey use direct access to the variables, will
    > that generate KVO notifications?

    Yes, assuming that there is an observer for the keypath @"myArray".
    For example, calling [[myObject mutableArrayValueForKey:@"myArray"]
    addObject:@"test"] will trigger -will/
    didChange:valueAtIndexes:forKey: notifications. The observer's -
    observeValueForKeyPath:ofObject:change:context: method will be called
    with change type NSKeyValueChangeInsertion.

    -Jeff
previous month august 2008 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Go to today