I spent some time on this problem a couple months ago and found some code on cocoadev which I improved upon, also added a little test code. There still may be bugs in it.

I don't know what the byte/character limit is on this list, but at least the header should make it through.

-------------

#import <Cocoa/Cocoa.h>

/*!
 @brief    Categories for making mutable deep copies of dictionaries,
 arrays and sets.

 @details   The original author says that this doesn't work:
 "... for the life of me I can't figure out why, it seems that -copy
 is always a copy of the pointers. Examining two dictionaries in the
 debugger using this method shows that the objects have different
 pointers but their components are identical, STILL. So it seems not
 to work."

 Note that another way to make a deep copy is
 make an archive of it and immediately unarchive it into a
 different variable. The only down-side is that the object to
 be copied must implement NSCoding.

 To do that,
 id foo = ... ;
NSData* fooArchive = [NSKeyedArchiver archivedDataWithRootObject:foo] ;
 id fooCopy = [NSKeyedUnarchiver unarchiveObjectWithData:fooArchive] ;

Source: http://www.cocoadev.com/index.pl? MutableDeepCopyAndUserDefaults
 */

/*!
 @brief    A category of NSObject which produces deep copies
 of collections containing dictionaries, arrays and/or sets.

 @details
 */


/*!
 @brief    Rule for how to clone leaf nodes when making a deep copy.

 @details  These are listed in order from least stringent
 to most stringent.
*/
typedef NSInteger SSYCloneStyleBitmask ;

extern SSYCloneStyleBitmask const SSYCloneStyleBitmaskCopy ;
extern SSYCloneStyleBitmask const SSYCloneStyleBitmaskMutable ;
extern SSYCloneStyleBitmask const SSYCloneStyleBitmaskEncodeable ;
extern SSYCloneStyleBitmask const SSYCloneStyleBitmaskSerializable ;

@interface NSObject (DeepCloning)

/*!
 @brief    Returns a deep, mutable copy of the receiver

 @details  If an object responds to -mutableCopyWithZone and
 -count, it is treated as a container node.&nbsp;

 Leaf nodes are treated as follows:
 <ul>
 <li>If the style mask specifies serializable but an object is not
 serializable, its image in the returned result will be its
 -description.</li>
 <li>Else, if the style mask specifies encodeable but an object is not
 encodeable with NSKeyedArchiver, its image in the returned result
 will be its -description.</li>
 <li>Else, if the style mask specifies mutable and the object responds
 to -mutableCopyWithZone:, its image in the returned result will be a
 mutable copy of the object.</li>
 <li>Else, if the style mask specifies copy and the object responds to
 -copyWithZone:, its image the returned result will be a copy
 of the object.</li>
 <li>Else, its image in the returned result is the object itself.</li>
 </ul>

 @param    style  Determines the makeup of non-collection
 objects in the result */
- mutableDeepCloneStyle:(SSYCloneStyleBitmask)style;

@end

#import "NSObject+DeepCloning.h"

SSYCloneStyleBitmask const SSYCloneStyleBitmaskCopy = 1 ;
SSYCloneStyleBitmask const SSYCloneStyleBitmaskMutable = 2 ;
SSYCloneStyleBitmask const SSYCloneStyleBitmaskEncodeable = 4 ;
SSYCloneStyleBitmask const SSYCloneStyleBitmaskSerializable = 8 ;

@implementation NSObject (DeepCloning)

- copyLeafStyle:(SSYCloneStyleBitmask)style {
    if(
       ((style & SSYCloneStyleBitmaskSerializable) != 0)
       &&
       ![NSPropertyListSerializationdataFromPropertyList:self
format:NSPropertyListBinaryFormat_v1_0
                                         errorDescription:NULL]
       ) {
        // Invoker specified serializable but self is not serializable.
        // Return a description
        return [[self description] retain] ;
    }
    else if(
            ((style & SSYCloneStyleBitmaskMutable) != 0)
            &&
            ![NSKeyedArchiver archivedDataWithRootObject:self]
            ) {
        // Invoker specified mutable and self is mutable
        // Return a mutable copy
        return [[self description] retain] ;
    }
    else if(
            ((style & SSYCloneStyleBitmaskCopy) != 0)
            &&
            [self respondsToSelector:@selector(copyWithZone:)]
            ) {
        // Invoker specified copy and self is copyable
        // Return a copy
        return [self copy];
    }
    else {
        // Return self
        return [self retain];
    }
}

- mutableDeepCloneStyle:(SSYCloneStyleBitmask)style {
    if (
        [self respondsToSelector:@selector(mutableCopyWithZone:)]
        &&
        [self respondsToSelector:@selector(count)]) {
        return [self mutableCopy] ;
    }
    else {
        return [self copyLeafStyle:style] ;
    }

    // Supress compiler warning
    return nil ;
}

@end

@implementation NSDictionary (DeepCloning)

- mutableDeepCloneStyle:(SSYCloneStyleBitmask)style {
NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] init];
    for (id key in self) {
        id obj = [[self objectForKey:key] mutableDeepCloneStyle:style];
        [newDictionary setObject:obj forKey:key];
        [obj release];
    }
    return newDictionary;
}

@end


@implementation NSArray (DeepCloning)

- mutableDeepCloneStyle:(SSYCloneStyleBitmask)style {
    NSMutableArray *newArray = [[NSMutableArray alloc] init];
    for (id object in self) {
        [newArray addObject:object];
        [object release];
    }
    return newArray;
}

@end


@implementation NSSet (DeepCloning)

- mutableDeepCloneStyle:(SSYCloneStyleBitmask)style {
    NSMutableSet *newSet = [[NSMutableSet alloc] init];
    for (id object in self) {
        [newSet addObject:object];
        [object release];
    }
    return newSet;
}

@end

#ifdef TEST_CODE_FOR_NSOBJECT_DEEP_CLONING
#import "NSDictionary+KeyPaths.h"

@interface Foo : NSObject {

}

@end

@implementation Foo

- (NSString*)description {
    return @"This is a Foo." ;
}

@end


int main(int argc, const char *argv[]) {

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init] ;
    NSMutableDictionary* md = [NSMutableDictionary dictionary] ;
    [md setValue:@"red"
      forKeyPath:@"meals.lunch.fruit.color"] ;
    [md setValue:[[[Foo alloc] init] autorelease]
      forKeyPath:@"meals.lunch.cheese.color"] ;
    NSLog(@"original = %@", md) ;

    [md serializableDictionary] ;

NSMutableDictionary* mdc = [md mutableDeepCloneStyle:SSYCloneStyleBitmaskSerializable] ;
    NSLog(@"mdc = %@", mdc) ;


    [pool release] ;

    return 0 ;
}

#endif
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to