Yesterday, we discussed loading a (mutable) plist from disk. To review: there are built-in Cocoa functions that load plists, but they return immutable data structures. (Happily, Core Foundation functions exist that can create mutable plists from XML files.) Suppose you wanted to create mutable structures from the immutable ones returned by the Cocoa functions? The built-in mutableCopy
functions only do shallow copies, so you’d have write a little code. To which we now turn.
Example
To review yesterday’s example, let’s assume you have a plist stored on disk in the the following XML file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>alpha</key>
<array>
<integer>0</integer>
<integer>1</integer>
<integer>2</integer>
</array>
<key>bravo</key>
<array>
<integer>3</integer>
<integer>4</integer>
<integer>5</integer>
</array>
<key>charlie</key>
<array>
<integer>6</integer>
<integer>7</integer>
<integer>8</integer>
</array>
</dict>
</plist>
Let’s also assume that you load it with the following code:
// Assume that path is the pathname of a file with the XML contents shown above
NSDictionary* elements = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
How can you convert elements
to a deeply mutable data structure?
Categories
To perform this immutable to mutable conversion, you’ll need a set of deep copying functions. The cleanest approach is to use Objective-C’s categories to extend the NSDictionary
, NSArray
, NSString
, NSDate
, NSData
, and NSNumber
classes to have deepMutableCopy
methods. (Since NSDate
and NSNumber
don’t have mutable analogs, these classes’ deepMutableCopy
methods will just do simple copies; the methods are included for symmetry.)
Here’s what the code might look like. First, add a DeepCopy.h
file to your project, with the category declarations:
#import <UIKit/UIKit.h>
@interface NSDictionary (DeepMutableCopy)
- (id)deepMutableCopy;
@end
@interface NSArray (DeepMutableCopy)
- (id)deepMutableCopy;
@end
@interface NSString (DeepMutableCopy)
- (id)deepMutableCopy;
@end
@interface NSDate (DeepMutableCopy)
- (id)deepMutableCopy;
@end
@interface NSData (DeepMutableCopy)
- (id)deepMutableCopy;
@end
@interface NSNumber (DeepMutableCopy)
- (id)deepMutableCopy;
@end
Then, add a DeepCopy.m
file to your project, with the category definitions:
#import "DeepCopy.h"
@implementation NSDictionary (DeepMutableCopy)
- (id)deepMutableCopy
{
NSMutableDictionary* rv = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
NSArray* keys = [self allKeys];
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
for (id k in keys)
{
[rv setObject:[[[self valueForKey:k] deepMutableCopy] autorelease] forKey:k];
}
[pool release];
return rv;
}
@end
@implementation NSArray (DeepMutableCopy)
- (id)deepMutableCopy
{
int n = [self count];
NSMutableArray* rv = [[NSMutableArray alloc] initWithCapacity:n];
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
for (int i = 0; i < n; i++)
{
[rv insertObject:[[[self objectAtIndex:i] deepMutableCopy] autorelease] atIndex:i];
}
[pool release];
return rv;
}
@end
@implementation NSString (DeepMutableCopy)
- (id)deepMutableCopy
{
return [self mutableCopy];
}
@end
@implementation NSDate (DeepMutableCopy)
- (id)deepMutableCopy
{
return [self copy];
}
@end
@implementation NSData (DeepMutableCopy)
- (id)deepMutableCopy
{
return [self mutableCopy];
}
@end
@implementation NSNumber (DeepMutableCopy)
- (id)deepMutableCopy
{
return [self copy];
}
@end
Now, you can use the deepMutableCopy
method to - ah - make a deep mutable copy of elements
:
#import "DeepCopy.h"
// ... snip ...
// Assume that path is the pathname of a file with the XML contents shown above
NSDictionary* elements = [[NSDictionary dictionaryWithContentsOfFile:path] deepMutableCopy];
Pingback: Things that were not immediately obvious to me » Blog Archive » Deep Copying (Bug Fix)
Pingback: Things that were not immediately obvious to me » Blog Archive » Deep Copying (Built-In)