Leopard Properties and NSMutable Array

  • Hi All,

    This is my first post so I hop I'm doing this right!

    I'm working on a Leopard app and have started to use properties.  In
    one of my classes I have an NSMutableArray which I have declared in my
    @interface as:

    @property(copy) NSMutableArray *content;

    the ivar is then @synthesized in my @implentation.

    in my -init method I do:

    self. content = [[NSMutableArray alloc] init];
    [self. content addObject:[NSString string];

    But I get the an error that tells me that I'm trying to mutate an
    immutable collection.  Why is this, is the setter calling a -copy
    method which returns an immutable copy? If so, why have this behaviour?

    Which argument to the @property directive would give the same as

    - (void)setContent:(NSArray *)newArray;
    {
    if(content != newArray) {
      [content release];
      content = [[NSMutableArray alloc] initWithArray:newArray];
    }
    }

    Thanks!
  • On Jan 5, 2008, at 5:41 PM, Jonathan wrote:
    > self. content = [[NSMutableArray alloc] init];
    > [self. content addObject:[NSString string];
    >
    > But I get the an error that tells me that I'm trying to mutate an
    > immutable collection.  Why is this, is the setter calling a -copy
    > method which returns an immutable copy? If so, why have this
    > behaviour?

    That is because the synthesized method sends -copy to the mutable
    array, yielding an immutable array.

    > Which argument to the @property directive would give the same as
    >
    > - (void)setContent:(NSArray *)newArray;
    > {
    > if(content != newArray) {
    > [content release];
    > content = [[NSMutableArray alloc] initWithArray:newArray];
    > }
    > }

    Which, unfortunately, is the alternative.  There is an enhancement
    request already to offer a mutablecopy keyword.

    b.bum
  • Thanks alot!  Thought my inexperience was rearing its ugly head.

    Much appreciated

    On 6 Jan 2008, at 01:53, Bill Bumgarner wrote:

    > On Jan 5, 2008, at 5:41 PM, Jonathan wrote:
    >> self. content = [[NSMutableArray alloc] init];
    >> [self. content addObject:[NSString string];
    >>
    >> But I get the an error that tells me that I'm trying to mutate an
    >> immutable collection.  Why is this, is the setter calling a -copy
    >> method which returns an immutable copy? If so, why have this
    >> behaviour?
    >
    > That is because the synthesized method sends -copy to the mutable
    > array, yielding an immutable array.
    >
    >> Which argument to the @property directive would give the same as
    >>
    >> - (void)setContent:(NSArray *)newArray;
    >> {
    >> if(content != newArray) {
    >> [content release];
    >> content = [[NSMutableArray alloc] initWithArray:newArray];
    >> }
    >> }
    >
    > Which, unfortunately, is the alternative.  There is an enhancement
    > request already to offer a mutablecopy keyword.
    >
    > b.bum
  • On Jan 5, 2008, at 5:53 PM, Bill Bumgarner wrote:
    >>
    >> Which argument to the @property directive would give the same as
    >>
    >> - (void)setContent:(NSArray *)newArray;
    >> {
    >> if(content != newArray) {
    >> [content release];
    >> content = [[NSMutableArray alloc] initWithArray:newArray];
    >> }
    >> }
    >
    > Which, unfortunately, is the alternative.  There is an enhancement
    > request already to offer a mutablecopy keyword.

    I should add, though, that the above is not really atomic (and, of
    course, "atomic" is not thread safe -- merely non-crashy in the face
    of the threads).

    (One of the advantages of GC is that simple setters like the above
    become atomic in that assignments are atomic for object types.)

    b.bum
  • On 06/01/2008, at 12:53 PM, Bill Bumgarner wrote:

    > Which, unfortunately, is the alternative.  There is an enhancement
    > request already to offer a mutablecopy keyword.

    Is there actually a use for a 'mutablecopy' keyword?  I ran into the
    same problem as Jonathan, but then realised that retain semantics are
    typically what you want for a mutable object.  I can't quite think of
    a case where a mutable copy of the array in the class would actually
    be useful.

    (Maybe that thought can be used as a possible warning: if you define a
    property for a type that obeys the NSMutableCopying protocol, perhaps
    a warning can be emitted if you use copy rather than retain...)

    --
    % Andre Pang : trust.in.love.to.save  <http://www.algorithm.com.au/>
  • On Jan 5, 2008, at 7:10 PM, André Pang wrote:
    > On 06/01/2008, at 12:53 PM, Bill Bumgarner wrote:
    >> Which, unfortunately, is the alternative.  There is an enhancement
    >> request already to offer a mutablecopy keyword.
    > Is there actually a use for a 'mutablecopy' keyword?  I ran into the
    > same problem as Jonathan, but then realised that retain semantics
    > are typically what you want for a mutable object.  I can't quite
    > think of a case where a mutable copy of the array in the class would
    > actually be useful.
    >
    > (Maybe that thought can be used as a possible warning: if you define
    > a property for a type that obeys the NSMutableCopying protocol,
    > perhaps a warning can be emitted if you use copy rather than
    > retain...)

    There are uses for mutablecopy in that there has been more than one
    person that has filed a request for it. :)

    Seriously, though, there are cases where mutablecopy makes a lot of
    sense.  Specifically, if your object model is such that your object
    wants to take ownership of the contents of the array and changes
    therein.  It is much easier to guarantee that a mutable array's
    contents haven't changed if you have isolated ownership of said
    mutable array;  copying on set and returning a copy on get.

    Of course, that has efficiency / performance / memory implications
    and, thus, balance must be maintained between performance and bullet
    proof.

    b.bum
  • On Jan 5, 2008, at 7:10 PM, André Pang wrote:

    > Is there actually a use for a 'mutablecopy' keyword?  I ran into the
    > same problem as Jonathan, but then realised that retain semantics
    > are typically what you want for a mutable object.  I can't quite
    > think of a case where a mutable copy of the array in the class would
    > actually be useful.

    Retaining a mutable object means you have no idea how its collection
    will change outside of your control. It doesn't matter as much with
    immutable objects, of course. NSMutableString is another good example
    of needing a mutable copy, rather than just a retain.

    Design-wise, you could decide to never have mutable objects as
    instance variables and just re-set the entire array each time -- but
    that seems a bit odd when you have thousands of items in a collection,
    since each of the members would likely need to get a retain when you
    duplicate it. It might also make it difficult to bind the array to an
    NSArrayController.

          - Scott_______________________________________________
    MacOSX-dev mailing list
    <MacOSX-dev...>
    http://www.omnigroup.com/mailman/listinfo/macosx-dev
  • On 06/01/2008, at 2:43 PM, Bill Bumgarner wrote:

    > Seriously, though, there are cases where mutablecopy makes a lot of
    > sense.  Specifically, if your object model is such that your object
    > wants to take ownership of the contents of the array and changes
    > therein.

    Right, agreed.  I did think of that case, but thought that it was a
    little unusual in that you would have to access the ivar directly
    inside your class's code to update its contents, rather than using the
    property syntax (otherwise you'd be making a mutable copy of the
    object on every update).  So mutable-copy properties requires some
    care in usage, but I suppose it's no more care needed than with retain
    semantics.

    While we're on the subject of properties, I wonder if I can subvert
    the topic a bit...

    I was pondering submitting an enhancement request to have a "non-nil"
    property declaration, e.g.

      @property (retain, nonnil) id foo;

    That declaration would only apply to Objective-C objects[1], and hint
    to the user that setting the property to a nil object is illegal.
    @synthesize would be expected to check if the object being passed in
    is nil; if it is, it can throw an exception or something else sensible.

    I think non-nil setters are a common-enough scenario that it'd justify
    being an additional property declaration.  Right now I'm resorting to
    writing my own setters for properties that I want to be non-nil, which
    is OK, but hey, the less work I have to do, the better :).

    I'm happy to submit a formal radar request for this, but I thought I'd
    check with others first whether I'm on crack.

    1. I realise that being only applicable to Objective-C objects
    restricts the generality of the property declaration, but hey, assign/
    retain/copy(/mutablecopy) are all object-specific already anyway :).

    --
    % Andre Pang : trust.in.love.to.save  <http://www.algorithm.com.au/>
  • On Jan 5, 2008, at 8:07 PM, André Pang wrote:

    > Right now I'm resorting to writing my own setters for properties
    > that I want to be non-nil, which is OK, but hey, the less work I
    > have to do, the better :)

    Key-Value Validation may help:
    <http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Co
    ncepts/Validation.html
    >

        - Scott_______________________________________________
    MacOSX-dev mailing list
    <MacOSX-dev...>
    http://www.omnigroup.com/mailman/listinfo/macosx-dev
  • On 06/01/2008, at 5:18 PM, Scott Stevenson wrote:

    >> Right now I'm resorting to writing my own setters for properties
    >> that I want to be non-nil, which is OK, but hey, the less work I
    >> have to do, the better :)
    >
    > Key-Value Validation may help:
    > <http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Co
    ncepts/Validation.html
    > >

    I looked at that.  However, that documentation says that Key-Value
    Validation methods are not called when a property is set:

      "Key-value coding does not perform validation automatically. It is,
    in general, your application’s responsibility to invoke the validation
    methods"

    and:

      "Warning: An implementation of -set<Key>: for a property should
    never call the validation methods."

    It feels like Key-Value Validation is designed for higher-level
    purposes than making sure an object is non-nil in a setter.  From what
    I've read, it seems like the setter methods are the correct place to
    check for that.

    --
    % Andre Pang : trust.in.love.to.save  <http://www.algorithm.com.au/>
  • that use of the term "property" I believe predates Objective-C 2.0
    Properties

    I don't believe it is intended to imply Objective-C 2.0 Properties
    specifically.

    On Jan 6, 2008, at 1:28 AM, André Pang wrote:

    > On 06/01/2008, at 5:18 PM, Scott Stevenson wrote:
    >
    >>> Right now I'm resorting to writing my own setters for properties
    >>> that I want to be non-nil, which is OK, but hey, the less work I
    >>> have to do, the better :)
    >>
    >> Key-Value Validation may help:
    >> <http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Co
    ncepts/Validation.html
    >> >
    >
    > I looked at that.  However, that documentation says that Key-Value
    > Validation methods are not called when a property is set:
    >
    > "Key-value coding does not perform validation automatically. It is,
    > in general, your application’s responsibility to invoke the
    > validation methods"
    >
    > and:
    >
    > "Warning: An implementation of -set<Key>: for a property should
    > never call the validation methods."
    >
    > It feels like Key-Value Validation is designed for higher-level
    > purposes than making sure an object is non-nil in a setter.  From
    > what I've read, it seems like the setter methods are the correct
    > place to check for that.
    >
    >
    > --
    > % Andre Pang : trust.in.love.to.save  <http://www.algorithm.com.au/>
    >
    >
    >
    > _______________________________________________
    > MacOSX-dev mailing list
    > <MacOSX-dev...>
    > http://www.omnigroup.com/mailman/listinfo/macosx-dev
  • On 6 Jan 2008, at 03:43, Bill Bumgarner <bbum...> wrote:

    > On Jan 5, 2008, at 7:10 PM, André Pang wrote:
    >> On 06/01/2008, at 12:53 PM, Bill Bumgarner wrote:
    >>> Which, unfortunately, is the alternative.  There is an
    >>> enhancement request already to offer a mutablecopy keyword.
    >> Is there actually a use for a 'mutablecopy' keyword?  I ran into
    >> the same problem as Jonathan, but then realised that retain
    >> semantics are typically what you want for a mutable object.  I
    >> can't quite think of a case where a mutable copy of the array in
    >> the class would actually be useful.
    >>
    >> (Maybe that thought can be used as a possible warning: if you
    >> define a property for a type that obeys the NSMutableCopying
    >> protocol, perhaps a warning can be emitted if you use copy rather
    >> than retain...)
    >
    > There are uses for mutablecopy in that there has been more than one
    > person that has filed a request for it. :)
    >
    > Seriously, though, there are cases where mutablecopy makes a lot of
    > sense.  Specifically, if your object model is such that your object
    > wants to take ownership of the contents of the array and changes
    > therein.  It is much easier to guarantee that a mutable array's
    > contents haven't changed if you have isolated ownership of said
    > mutable array;  copying on set and returning a copy on get.
    >
    > Of course, that has efficiency / performance / memory implications
    > and, thus, balance must be maintained between performance and bullet
    > proof.
    >
    > b.bum
    >
    And this is the thing. I don't want to merely retain it as I can see
    that I'd accidentally mutate it further down the line.  Looks like
    some properties I'll write myself.

    Thanks again guys, the discussion is really useful for me.

    Jon_______________________________________________
    MacOSX-dev mailing list
    <MacOSX-dev...>
    http://www.omnigroup.com/mailman/listinfo/macosx-dev
  • On 6 Jan 2008, at 03:43, Bill Bumgarner wrote:

    > On Jan 5, 2008, at 7:10 PM, André Pang wrote:
    >> On 06/01/2008, at 12:53 PM, Bill Bumgarner wrote:
    >>> Which, unfortunately, is the alternative.  There is an
    >>> enhancement request already to offer a mutablecopy keyword.
    >> Is there actually a use for a 'mutablecopy' keyword?  I ran into
    >> the same problem as Jonathan, but then realised that retain
    >> semantics are typically what you want for a mutable object.  I
    >> can't quite think of a case where a mutable copy of the array in
    >> the class would actually be useful.
    >>
    >> (Maybe that thought can be used as a possible warning: if you
    >> define a property for a type that obeys the NSMutableCopying
    >> protocol, perhaps a warning can be emitted if you use copy rather
    >> than retain...)
    >
    > There are uses for mutablecopy in that there has been more than one
    > person that has filed a request for it. :)
    >
    > Seriously, though, there are cases where mutablecopy makes a lot of
    > sense.  Specifically, if your object model is such that your object
    > wants to take ownership of the contents of the array and changes
    > therein.  It is much easier to guarantee that a mutable array's
    > contents haven't changed if you have isolated ownership of said
    > mutable array;  copying on set and returning a copy on get.
    >
    > Of course, that has efficiency / performance / memory implications
    > and, thus, balance must be maintained between performance and bullet
    > proof.
    >
    > b.bum
    >

    What I really wanted was the ability to declare the mutable array as a
    property as a I've really grown to like the 'dot syntax'.  So here is
    the solution that seems to have worked:

    @interface Foo : NSObject {
    NSMutableArray *array;
    }

    @property(copy) NSMutableArray *array;

    - (void)setArray:(NSMutableArray *)a;
    - (NSMutableArray *)array;

    @end

    @implementation

    @dynamic array;

    - (void)setArray:(NSMutableArray *)a;
    {
    if (array != a) {
      [array release];
      array = [a mutableCopy];
    }
    }

    - (NSMutableArray *)array;
    {
    [return array];
    }

    @end

    In my main.m I can then call [array addObject:[NSString string]];
    without an error or compiler warnings.

    I think I could pass the setter an NSArray too as NSArray conforms to
    NSMutableCopying.  Is there anything that I missed, could this go
    horribly wrong somewhere?

    Jon_______________________________________________
    MacOSX-dev mailing list
    <MacOSX-dev...>
    http://www.omnigroup.com/mailman/listinfo/macosx-dev
  • On 6 Jan 2008, at 03:43, Bill Bumgarner wrote:

    > On Jan 5, 2008, at 7:10 PM, André Pang wrote:
    >> On 06/01/2008, at 12:53 PM, Bill Bumgarner wrote:
    >>> Which, unfortunately, is the alternative.  There is an
    >>> enhancement request already to offer a mutablecopy keyword.
    >> Is there actually a use for a 'mutablecopy' keyword?  I ran into
    >> the same problem as Jonathan, but then realised that retain
    >> semantics are typically what you want for a mutable object.  I
    >> can't quite think of a case where a mutable copy of the array in
    >> the class would actually be useful.
    >>
    >> (Maybe that thought can be used as a possible warning: if you
    >> define a property for a type that obeys the NSMutableCopying
    >> protocol, perhaps a warning can be emitted if you use copy rather
    >> than retain...)
    >
    > There are uses for mutablecopy in that there has been more than one
    > person that has filed a request for it. :)
    >
    > Seriously, though, there are cases where mutablecopy makes a lot of
    > sense.  Specifically, if your object model is such that your object
    > wants to take ownership of the contents of the array and changes
    > therein.  It is much easier to guarantee that a mutable array's
    > contents haven't changed if you have isolated ownership of said
    > mutable array;  copying on set and returning a copy on get.
    >
    > Of course, that has efficiency / performance / memory implications
    > and, thus, balance must be maintained between performance and bullet
    > proof.
    >
    > b.bum
    >

    What I really wanted was the ability to declare the mutable array as a
    property as a I've really grown to like the 'dot syntax'.  So here is
    the solution that seems to have worked:

    @interface Foo : NSObject {
    NSMutableArray *array;
    }

    @property(copy) NSMutableArray *array;

    - (void)setArray:(NSMutableArray *)a;
    - (NSMutableArray *)array;

    @end

    @implementation

    @dynamic array;

    - (void)setArray:(NSMutableArray *)a;
    {
    if (array != a) {
      [array release];
      array = [a mutableCopy];
    }
    }

    - (NSMutableArray *)array;
    {
    [return array];
    }

    @end

    In my main.m I can then call [array addObject:[NSString string]];
    without an error or compiler warnings.

    I think I could pass the setter an NSArray too as NSArray conforms to
    NSMutableCopying.  Is there anything that I missed, could this go
    horribly wrong somewhere?

    Jon_______________________________________________
    MacOSX-dev mailing list
    <MacOSX-dev...>
    http://www.omnigroup.com/mailman/listinfo/macosx-dev
  • On Jan 6, 2008, at 6:55 AM, Jonathan Dann wrote:

    > What I really wanted was the ability to declare the mutable array as
    > a property as a I've really grown to like the 'dot syntax'.  So here
    > is the solution that seems to have worked:

    The dot syntax works fine for non-property based attributes. I.e. you
    could drop the @property declaration below and all your code -- dot or
    no dot -- would continue to work the same.

    What would change, though, is that the additional metadata provided by
    the @property declaration would no longer be available at runtime for
    introspection purposes.  At this time, there are few, if any, bits of
    code that use that metadata.

    I expect that'll change in the future.

    > - (NSMutableArray *)array;
    > {
    > return array;
    > }

    At the least, that should be...

    return [array autorelease];

    ... unless you have GC turned on (which the use of retain/release in
    the rest of your code indicates that you likely do not).

    Given the overall defensiveness of your code, you might consider
    returning a copy of your array, mutable or otherwise.  As it stands,
    any client can grab a reference to the internal storage of your class
    and change it.

    return [[array copy] autorelease];

    > In my main.m I can then call [array addObject:[NSString string]];
    > without an error or compiler warnings.
    >
    > I think I could pass the setter an NSArray too as NSArray conforms
    > to NSMutableCopying.  Is there anything that I missed, could this go
    > horribly wrong somewhere?

    Nope -- that will work fine.

    b.bum
  • On Jan 6, 2008, at 9:57 AM, Jonathan Dann wrote:

    > What I really wanted was the ability to declare the mutable array as
    > a property as a I've really grown to like the 'dot syntax'

    <http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/Articl
    es/chapter_5_section_4.html
    >

    "Accessing a property property calls the get method associated with
    the property (by default, property) and setting it calls the set
    method associated with the property (by default, setProperty:). In
    fact, as long as there is an appropriate getter and setter available,
    you can use the dot-syntax invoke those methods even if you haven’t
    declared a property (if you haven’t declared a property, the getter
    and setter must be named property and setProperty: respectively)."

    mmalc
  • On Jan 6, 2008, at 11:05 AM, Bill Bumgarner wrote:
    > At the least, that should be...
    >
    > return [array autorelease];

    Geez.  I have been doing so much GC that even one cup of coffee no
    longer prevents such stupidity.

    That should, of course, be:

    return [[array retain] autorelease];

    Doh!

    b.bum
  • > At the least, that should be...
    >
    > return [array autorelease];
    >
    > ... unless you have GC turned on (which the use of retain/release in
    > the rest of your code indicates that you likely do not).
    >
    > Given the overall defensiveness of your code, you might consider
    > returning a copy of your array, mutable or otherwise.  As it stands,
    > any client can grab a reference to the internal storage of your class
    > and change it.
    >
    > return [[array copy] autorelease];

    Thanks Bill, that does seem better, I'm quite new to all of this, and
    I'm being quite cautious with my memory managment, trying to get it
    *really* right.  Your explanation makes perfect sense.

    Jon
  • On Jan 5, 2008, at 9:16 PM, Bill Bumgarner wrote:

    > I should add, though, that the above is not really atomic (and, of
    > course, "atomic" is not thread safe -- merely non-crashy in the
    > face of the threads).
    >
    > (One of the advantages of GC is that simple setters like the above
    > become atomic in that assignments are atomic for object types.)

    Can you offer any advice for the times when I must write my own
    accessor, but want it to have similar behaviors to the autogenerated
    accessor? In particular, I'm thinking about the default atomic
    behavior. Is this simply wrapping the work in a @synchronized(self)
    block? Something else?

    I'd look in the objc-2 sources, but they aren't available yet :-)

    Jim
  • On Jan 6, 2008, at 11:32 AM, Jim Correia wrote:
    > Can you offer any advice for the times when I must write my own
    > accessor, but want it to have similar behaviors to the autogenerated
    > accessor? In particular, I'm thinking about the default atomic
    > behavior. Is this simply wrapping the work in a @synchronized(self)
    > block? Something else?

    Sure.

    http://www.friday.com/bbum/2008/01/13/objectivce-c-atomic-properties-thread
    ing-andor-custom-settergetter/


    Long and short of it, you can use @synchronized(self) to make an
    accessor pair atomic.

    But does that actually solve a problem you have?  Probably not.

    b.bum
previous month january 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