ARC Extending Lifetime of Objects

  • Suppose, I have C-arrays defined as follows:

    int count = [dictionary count];
    id __unsafe_unretained values[count];
    id __unsafe_unretained keys[count];

    I fill these arrays with:

    [dictionary getObjects:values andKeys:keys];

    Now, I would like to "reuse" these arrays through holding objects maintained by ARC (if possible):

    for (int i = 0; i < count; ++i) {
        keys[i] = [keys[i] copy];
        values[i] = [values[i]] copy];
    }
    NSDictionary *other = [NSDictionary dictionaryWithObjects:values forKeys:keys count:count];

    This won't work in ARC, since the arrays are declared __unsafe_unretained and this would cause the newly created object immediately be deallocated once it is assigned to the array's element.

    What I would like to avoid is to use separate arrays (appropriately declared), and also avoid to disable ARC and maintain the release counts manually.

    Is there a way to extend the lifetime of the created objects, or is there a way to "re-declare" the arrays so that ARC can maintain the references - or is there any other simple "trick"?

    Thanks for suggestions!  ;)

    Andreas
  • On 23 Jul 2012, at 09:00, Andreas Grosam <agrosam...> wrote:

    > Suppose, I have C-arrays defined as follows:
    >
    > int count = [dictionary count];
    > id __unsafe_unretained values[count];
    > id __unsafe_unretained keys[count];
    >
    >
    > I fill these arrays with:
    >
    > [dictionary getObjects:values andKeys:keys];
    >
    >
    >
    > Now, I would like to "reuse" these arrays through holding objects maintained by ARC (if possible):
    >
    > for (int i = 0; i < count; ++i) {
    > keys[i] = [keys[i] copy];
    > values[i] = [values[i]] copy];
    > }
    > NSDictionary *other = [NSDictionary dictionaryWithObjects:values forKeys:keys count:count];
    >
    >
    >
    > This won't work in ARC, since the arrays are declared __unsafe_unretained and this would cause the newly created object immediately be deallocated once it is assigned to the array's element.
    >
    > What I would like to avoid is to use separate arrays (appropriately declared), and also avoid to disable ARC and maintain the release counts manually.
    >
    > Is there a way to extend the lifetime of the created objects, or is there a way to "re-declare" the arrays so that ARC can maintain the references - or is there any other simple "trick"?
    >
    >
    > Thanks for suggestions!  ;)

    I think all this begs the questions:

    - Why do you want to get these objects stored in arrays rather than a dictionary?
    - Why C arrays, rather than NSArray?
  • On 23.07.2012, at 10:36, Mike Abdullah wrote:

    > I think all this begs the questions:
    >
    > - Why do you want to get these objects stored in arrays rather than a dictionary?
    > - Why C arrays, rather than NSArray?

    Well, I want to create a new dictionary from copies of elements from another dictionary. I cannot use simply copy or mutableCopy since I want to implement a "deep" copy.

    There is

    1)  + (id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keys

    and

    2)  + (id)dictionaryWithObjects:(const id[])objects forKeys:(const id < NSCopying >[])keys count:(NSUInteger)count

    for creation.

    I cannot use 1), e.g.

    NSArray* keys = [dictionary allKeys];
    NSArray* values = [dictionary allValues];

    since the order in values is not defined.

    Using
    - (void)getObjects:(id __unsafe_unretained [])objects andKeys:(id __unsafe_unretained [])keys
    however, returns objects in order according their association.

    Using the plain C-arry API and manual release/retain would be fine, but it won't work with ARC.

    Sure, I could use fast enumeration, and a separate NSArray for keys and values to accomplish what I want. But I was also hoping, the first approach is possibly faster. Using fast enumeration appears quite suboptimal for this task:

    NSMutableArray* keys = [NSMutableArray array];
    NSMutableArray* values = [NSMutableArray array];
    for (id key = dict] {
    id value = [dict objectForKey:key];
    // simplified!
    [keys addObject:[key deepCopy]];
    [values addObject:[value deepCopy]];
    }
    NSDictionary* result = [NSDictionary dictionaryWithObjects:values forKeys:keys];

    By the way: why don't we have:
    for (id<NSPair> p = dict] {
    [values addObject:[p.first deepCopy]];
    [keys addObject:[p.second deepCopy]];
    }
    (which might probably be faster)

    Andreas
  • On 23 Jul 2012, at 11:48, Andreas Grosam <agrosam...> wrote:

    >
    > On 23.07.2012, at 10:36, Mike Abdullah wrote:
    >
    >> I think all this begs the questions:
    >>
    >> - Why do you want to get these objects stored in arrays rather than a dictionary?
    >> - Why C arrays, rather than NSArray?
    >
    > Well, I want to create a new dictionary from copies of elements from another dictionary. I cannot use simply copy or mutableCopy since I want to implement a "deep" copy.

    Aha, now we're talking. You want -initWithDictionary:copyItems:
  • On 23.07.2012, at 13:09, Mike Abdullah wrote:

    > Aha, now we're talking. You want -initWithDictionary:copyItems:

    No, this is not a deep copy.

    Suppose, there is an element within the array, whose value is a mutable dictionary. If I change the that mutable dictionary (or its values), the copy and the dictionary would still compare equal to the copy (since children haven't been copied).

    Thus, I need to define a deepCopy method for NSDictionary (and NSArray, too).

    Andreas
  • On 23 Jul 2012, at 12:57, Andreas Grosam <agrosam...> wrote:

    >
    > On 23.07.2012, at 13:09, Mike Abdullah wrote:
    >
    >> Aha, now we're talking. You want -initWithDictionary:copyItems:
    >
    > No, this is not a deep copy.
    >
    > Suppose, there is an element within the array, whose value is a mutable dictionary. If I change the that mutable dictionary (or its values), the copy and the dictionary would still compare equal to the copy (since children haven't been copied).
    >
    >
    > Thus, I need to define a deepCopy method for NSDictionary (and NSArray, too).

    You should seriously consider whether your design is a good idea then. Copying the objects in an array is rare, but justifiable. Having multiple levels of nested dictionaries is rarely so. Sure you're not (ab)using dictionaries to avoid defining a new class?
  • On 23/07/2012, at 9:57 PM, Andreas Grosam wrote:

    > Thus, I need to define a deepCopy method for NSDictionary (and NSArray, too).

    So, do that.

    However, are you really sure? I remember thinking I needed this at one time and implemented a whole series of -deepCopy: methods in a variety of classes in a category on each. Works great, but in actual practice I think I used it once then figured out a better way to do what I wanted and no longer use this code, though it's still there if I ever do.

    Don't worry about performance until you can prove it's not good enough.

    --Graham
  • On 23.07.2012, at 14:04, Mike Abdullah wrote:

    >
    > On 23 Jul 2012, at 12:57, Andreas Grosam <agrosam...> wrote:
    >
    >>
    >> On 23.07.2012, at 13:09, Mike Abdullah wrote:
    >>
    >>> Aha, now we're talking. You want -initWithDictionary:copyItems:
    >>
    >> No, this is not a deep copy.
    >>
    >> Suppose, there is an element within the array, whose value is a mutable dictionary. If I change the that mutable dictionary (or its values), the copy and the dictionary would still compare equal to the copy (since children haven't been copied).
    >>
    >>
    >> Thus, I need to define a deepCopy method for NSDictionary (and NSArray, too).
    >
    > You should seriously consider whether your design is a good idea then. Copying the objects in an array is rare, but justifiable. Having multiple levels of nested dictionaries is rarely so. Sure you're not (ab)using dictionaries to avoid defining a new class?

    No, I don't think so. The NSDictionary/NSMutableDictionary is completely suitable for the purpose to represent that object which I have here. There is just no method to create a deep copy. (On the other hand, the isEqual method will effectively  "deep"  compare.)

    So, you might wonder why there is a need to have a deep copy? Well, how can I answer this without digressing? ;)

    But as an example:

    Suppose, there is a quite rich object represented as JSON. The JSON will be parsed and represented as a Foundation object, which is a NSDictionary. This is my object. Now suppose, there is the possibility to edit and make changes to this deep leveled object. Before making any changes in a GUI, I need to create a "Momento" (see, wiki <http://en.wikipedia.org/wiki/Memento_pattern> in order to save state of the original object and possibly be able to revert changes.

    Due to the lack of attributes attachable to properties in Objective-C language and a momento method (I already warned that I'll possibly digressing ;)  ) I just use a deep copy for creating the momento.

    Sure, I could use CoreData for accomplishing this as well …  but this was just an example.
  • On 23/07/2012, at 11:12 PM, Andreas Grosam wrote:

    > Due to the lack of attributes attachable to properties in Objective-C language and a momento method (I already warned that I'll possibly digressing ;)  ) I just use a deep copy for creating the momento.

    Another solution is to use archiving to turn your object graph into a block of data. While this may sound not very high performance, it may be adequate - after all, if the purpose is to Undo or Cancel, the actual need to Undo or Cancel doesn't arise very often, so it's not like you have to perform thousands of these per second, and archiving is pretty fast if your object graph isn't ultra-complex.

    --Graham
  • Dictionaries doesn't do deepCopy, but they do support NSCoding.
    Do you think you can use serialization to a NSData (a memory block) instead? It will have other benefit when you have too much undo items and want to page to a file. ;)

    -Jonny

    在 2012-7-23,下午9:12,Andreas Grosam <agrosam...> 写道:

    >
    > On 23.07.2012, at 14:04, Mike Abdullah wrote:
    >
    >>
    >> On 23 Jul 2012, at 12:57, Andreas Grosam <agrosam...> wrote:
    >>
    >>>
    >>> On 23.07.2012, at 13:09, Mike Abdullah wrote:
    >>>
    >>>> Aha, now we're talking. You want -initWithDictionary:copyItems:
    >>>
    >>> No, this is not a deep copy.
    >>>
    >>> Suppose, there is an element within the array, whose value is a mutable dictionary. If I change the that mutable dictionary (or its values), the copy and the dictionary would still compare equal to the copy (since children haven't been copied).
    >>>
    >>>
    >>> Thus, I need to define a deepCopy method for NSDictionary (and NSArray, too).
    >>
    >> You should seriously consider whether your design is a good idea then. Copying the objects in an array is rare, but justifiable. Having multiple levels of nested dictionaries is rarely so. Sure you're not (ab)using dictionaries to avoid defining a new class?
    >
    > No, I don't think so. The NSDictionary/NSMutableDictionary is completely suitable for the purpose to represent that object which I have here. There is just no method to create a deep copy. (On the other hand, the isEqual method will effectively  "deep"  compare.)
    >
    > So, you might wonder why there is a need to have a deep copy? Well, how can I answer this without digressing? ;)
    >
    > But as an example:
    >
    > Suppose, there is a quite rich object represented as JSON. The JSON will be parsed and represented as a Foundation object, which is a NSDictionary. This is my object. Now suppose, there is the possibility to edit and make changes to this deep leveled object. Before making any changes in a GUI, I need to create a "Momento" (see, wiki <http://en.wikipedia.org/wiki/Memento_pattern> in order to save state of the original object and possibly be able to revert changes.
    >
    > Due to the lack of attributes attachable to properties in Objective-C language and a momento method (I already warned that I'll possibly digressing ;)  ) I just use a deep copy for creating the momento.
    >
    > Sure, I could use CoreData for accomplishing this as well …  but this was just an example.
  • On 23.07.2012, at 15:22, Graham Cox wrote:

    And @Jonny

    > Another solution is to use archiving to turn your object graph into a block of data.

    Yes, I considered that option, too. This would keep inter-object references intact, too - while a (naive) deepCopy would force to copy any mutable object. I don't think, that mutable inter-object references will ever occur in that scenario, though.
  • On Jul 23, 2012, at 3:00 AM, Andreas Grosam wrote:

    > int count = [dictionary count];
    > id __unsafe_unretained values[count];
    > id __unsafe_unretained keys[count];

    > Now, I would like to "reuse" these arrays through holding objects maintained by ARC (if possible):
    >
    > for (int i = 0; i < count; ++i) {
    > keys[i] = [keys[i] copy];
    > values[i] = [values[i]] copy];
    > }

    > This won't work in ARC, since the arrays are declared __unsafe_unretained and this would cause the newly created object immediately be deallocated once it is assigned to the array's element.
    >
    > What I would like to avoid is to use separate arrays (appropriately declared), and also avoid to disable ARC and maintain the release counts manually.

    So you're aware of the simple and obvious solutions, but you have arbitrarily excluded them from consideration for no particular reason?  Why not just use a second pair of arrays?

    There's no trick you can use, I don't think.  The lifetime qualifiers define the semantics of the variables.  Even if you could temporarily override them, that would lead the compiler to do the wrong thing at other places.  It would over- or under-release things.

    Regards,
    Ken
  • On Jul 23, 2012, at 3:48 AM, Andreas Grosam <agrosam...> wrote:

    > Sure, I could use fast enumeration, and a separate NSArray for keys and values to accomplish what I want. But I was also hoping, the first approach is possibly faster. Using fast enumeration appears quite suboptimal for this task:

    Why not use -enumerateKeysAndObjectsUsingBlock:?

    --Kyle Sluder
  • I think this does what you want.  It works by [arguably ab-] using the CF bridging casts:

        NSUInteger count = [dictionary count];

        // Are you sure you don't want to malloc and free these? Might blow the
        // stack if they're large.
        id __unsafe_unretained values[count];
        id __unsafe_unretained keys[count];

        [dictionary getObjects:values andKeys:keys];

        for (NSUInteger i = 0; i < count; ++i) {
            keys[i] = (__bridge id)CFBridgingRetain([keys[i] copy]);
            values[i] = (__bridge id)CFBridgingRetain([values[i] copy]);
        }

        NSDictionary *other = [NSDictionary dictionaryWithObjects:values
                                                          forKeys:keys
                                                            count:count];

        for (NSUInteger i = 0; i < count; ++i) {
            CFBridgingRelease((__bridge CFTypeRef)keys[i]);
            CFBridgingRelease((__bridge CFTypeRef)values[i]);
        }

    I second the thoughts already expressed here though that doing:

        [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: dictionary]]

    would be more readable and more maintainable (and less likely to be wrong), so I'd say do that if performance does not prove to be an issue.

    Jamie.

    On 23 Jul 2012, at 09:00, Andreas Grosam wrote:

    > Suppose, I have C-arrays defined as follows:
    >
    > int count = [dictionary count];
    > id __unsafe_unretained values[count];
    > id __unsafe_unretained keys[count];
    >
    >
    > I fill these arrays with:
    >
    > [dictionary getObjects:values andKeys:keys];
    >
    >
    >
    > Now, I would like to "reuse" these arrays through holding objects maintained by ARC (if possible):
    >
    > for (int i = 0; i < count; ++i) {
    > keys[i] = [keys[i] copy];
    > values[i] = [values[i]] copy];
    > }
    > NSDictionary *other = [NSDictionary dictionaryWithObjects:values forKeys:keys count:count];
    >
    >
    >
    > This won't work in ARC, since the arrays are declared __unsafe_unretained and this would cause the newly created object immediately be deallocated once it is assigned to the array's element.
    >
    > What I would like to avoid is to use separate arrays (appropriately declared), and also avoid to disable ARC and maintain the release counts manually.
    >
    > Is there a way to extend the lifetime of the created objects, or is there a way to "re-declare" the arrays so that ARC can maintain the references - or is there any other simple "trick"?
    >
    >
    > Thanks for suggestions!  ;)
    >
    > Andreas
previous month july 2012 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