deep mutable copy of a NSMutableDictionary - objective-c

I am trying to create a deep-copy of a NSMutableDictionary and assign it to another NSMutableDictionary. The dictionary contains a bunch of arrays, each array containing names, and the key is an alphabet (the first letter of those names). So one entry in the dictionary is 'A' -> 'Adam', 'Apple'. Here's what I saw in a book, but I'm not sure if it works:
- (NSMutableDictionary *) mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc] initWithCapacity: [self count]];
NSArray *keys = [self allKeys];
for (id key in keys)
{
id oneValue = [self valueForKey:key]; // should return the array
id oneCopy = nil;
if ([oneValue respondsToSelector: #selector(mutableDeepCopy)])
{
oneCopy = [oneValue mutableDeepCopy];
}
if ([oneValue respondsToSelector:#selector(mutableCopy)])
{
oneCopy = [oneValue mutableCopy];
}
if (oneCopy == nil) // not sure if this is needed
{
oneCopy = [oneValue copy];
}
[ret setValue:oneCopy forKey:key];
//[oneCopy release];
}
return ret;
}
should the [onecopy release] be there or not?
Here's how I'm going to call this method:
self.namesForAlphabets = [self.allNames mutableDeepCopy];
Will that be ok? Or will it cause a leak? (assume that I declare self.namesForAlphabets as a property, and release it in dealloc).

Because of toll-free bridging, you can also use the CoreFoundation function CFPropertyListCreateDeepCopy:
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);

Assuming all elements of the array implement the NSCoding protocol, you can do deep copies via archiving because archiving will preserve the mutability of objects.
Something like this:
id DeepCopyViaArchiving(id<NSCoding> anObject)
{
NSData* archivedData = [NSKeyedArchiver archivedDataWithRootObject:anObject];
return [[NSKeyedUnarchiver unarchiveObjectWithData:archivedData] retain];
}
This isn't particularly efficient, though.

IMPORTANT: The question (and my code below) both deal with a very specific case, in which the NSMutableDictionary contains only arrays of strings. These solutions will not work for more complex examples. For more general case solutions, see the following:
Tom Dalling's answer
dreamlax's answer
Source from yfujiki on GitHub Gist
Answer for this specific case:
Your code should work, but you will definitely need the [oneCopy release]. The new dictionary will retain the copied objects when you add them with setValue:forKey, so if you do not call [oneCopy release], all of those objects will be retained twice.
A good rule of thumb: if you alloc, retain or copy something, you must also release it.
Note: here is some sample code that would work for certain cases only. This works because your NSMutableDictionary contains only arrays of strings (no further deep copying required):
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary * ret = [[NSMutableDictionary alloc]
initWithCapacity:[self count]];
NSMutableArray * array;
for (id key in [self allKeys])
{
array = [(NSArray *)[self objectForKey:key] mutableCopy];
[ret setValue:array forKey:key];
[array release];
}
return ret;
}

Another technique that I have seen (which is not at all very efficient) is to use an NSPropertyListSerialization object to serialise your dictionary, then you de-serialise it but specify that you want mutable leaves and containers.
NSString *errorString = nil;
NSData *binData =
[NSPropertyListSerialization dataFromPropertyList:self.allNames
format:NSPropertyListBinaryFormat_v1_0
errorString:&errorString];
if (errorString) {
// Something bad happened
[errorString release];
}
self.namesForAlphabets =
[NSPropertyListSerialization propertyListFromData:binData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:NULL
errorDescription:&errorString];
if (errorString) {
// something bad happened
[errorString release];
}
Again, this is not at all efficient.

Trying to figure out by checking respondToSelector(#selector(mutableCopy)) won't give the desired results as all NSObject-based objects respond to this selector (it's part of NSObject). Instead we have to query if an object conforms to NSMutableCopying or at least NSCopying. Here's my answer based on this gist mentioned in the accepted answer:
For NSDictionary:
#implementation NSDictionary (MutableDeepCopy)
// As seen here (in the comments): https://gist.github.com/yfujiki/1664847
- (NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *returnDict = [[NSMutableDictionary alloc] initWithCapacity:self.count];
NSArray *keys = [self allKeys];
for(id key in keys) {
id oneValue = [self objectForKey:key];
id oneCopy = nil;
if([oneValue respondsToSelector:#selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if([oneValue conformsToProtocol:#protocol(NSMutableCopying)]) {
oneCopy = [oneValue mutableCopy];
} else if([oneValue conformsToProtocol:#protocol(NSCopying)]){
oneCopy = [oneValue copy];
} else {
oneCopy = oneValue;
}
[returnDict setValue:oneCopy forKey:key];
}
return returnDict;
}
#end
For NSArray:
#implementation NSArray (MutableDeepCopy)
- (NSMutableArray *)mutableDeepCopy
{
NSMutableArray *returnArray = [[NSMutableArray alloc] initWithCapacity:self.count];
for(id oneValue in self) {
id oneCopy = nil;
if([oneValue respondsToSelector:#selector(mutableDeepCopy)]) {
oneCopy = [oneValue mutableDeepCopy];
} else if([oneValue conformsToProtocol:#protocol(NSMutableCopying)]) {
oneCopy = [oneValue mutableCopy];
} else if([oneValue conformsToProtocol:#protocol(NSCopying)]){
oneCopy = [oneValue copy];
} else {
oneCopy = oneValue;
}
[returnArray addObject:oneCopy];
}
return returnArray;
}
#end
Both methods have the same internal to-copy-or-not-to-copy logic and that could be extracted into a separate method but I left it like this for clarity.

For ARC - note kCFPropertyListMutableContainersAndLeaves for truly deep mutability.
NSMutableDictionary* mutableDict = (NSMutableDictionary *)
CFBridgingRelease(
CFPropertyListCreateDeepCopy(kCFAllocatorDefault,
(CFDictionaryRef)someNSDict,
kCFPropertyListMutableContainersAndLeaves));

Thought I'd update with an answer if you're using ARC.
The solution Weva has provided works just fine. Nowadays you could do it like this:
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFBridgingRelease(CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDict, kCFPropertyListMutableContainers));

Useful answers here, but CFPropertyListCreateDeepCopy doesn't handle [NSNull null] in the data, which is pretty normal with JSON decoded data, for example.
I'm using this category:
#import <Foundation/Foundation.h>
#interface NSObject (ATMutableDeepCopy)
- (id)mutableDeepCopy;
#end
Implementation (feel free to alter / extend):
#implementation NSObject (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return [self copy];
}
#end
#pragma mark - NSDictionary
#implementation NSDictionary (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return [NSMutableDictionary dictionaryWithObjects:self.allValues.mutableDeepCopy
forKeys:self.allKeys.mutableDeepCopy];
}
#end
#pragma mark - NSArray
#implementation NSArray (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
NSMutableArray *const mutableDeepCopy = [NSMutableArray new];
for (id object in self) {
[mutableDeepCopy addObject:[object mutableDeepCopy]];
}
return mutableDeepCopy;
}
#end
#pragma mark - NSNull
#implementation NSNull (ATMutableDeepCopy)
- (id)mutableDeepCopy
{
return self;
}
#end
Example extensions – strings are left as normal copies. You could override this if you want to be able to in place edit them. I only needed to monkey with a deep down dictionary for some testing, so I've not implemented that.

Related

Serialization Objective C not working

I'm trying to make an app with Objective C.
I'm trying to serialise an array existing out of objects and after wards deserialise it. Inside the object there are the methods
(id)initWithCoder:(NSCoder *)coder` and `encodeWithCoder:(NSCoder *)coder
But it seems the "rootObject" stays "nil" in the "loadDataFromDisk" -method
Here is my code :
#import "Alarm.h"
#implementation Alarm
#synthesize array = _array;
#synthesize time = _time;
#synthesize coder = _coder;
-(id)initWithCoder:(NSCoder *)coder
{
self = [super init];
if(self)
{
_array = [coder decodeObjectForKey:#"array"];
_time = [coder decodeObjectForKey:#"time"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:self.array forKey:#"array"];
[coder encodeObject:self.time forKey:#"time"];
}
#end
My save and load methods :
-(void)saveDataToDisk
{
NSString * path = [self pathForDataFile];
NSLog(#"Writing alarms to '%#' %lu", path, (unsigned long)array.count);
NSMutableDictionary * rootObject;
rootObject = [NSMutableDictionary dictionary];
[rootObject setValue:array forKey:#"alarms"];
[NSKeyedArchiver archiveRootObject:rootObject toFile:path];
}
-(void)loadDataFromDisk
{
NSString *path = [self pathForDataFile];
NSDictionary *rootObject = [NSMutableDictionary dictionary];
rootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// "array" is an array with Objects of "Alarm"
array = [rootObject valueForKey:#"alarms"];
NSLog(#"Loaded from : %# %lu",path ,(unsigned long)array.count);
}
I hope anyone can help me out with this.
Thanks in advance.
You #synthized the array backing store (ivar) as _array. So you need to access the array as either _array or self.array. In saveDataToDisk and loadDataFromDisk it is accessed as array.
To test your array coding try something simple like this:
NSLog(#"array: %#", self.array);
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.array];
NSLog(#"data: %#", data);
NSArray *recovered = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSLog(#"recovered: %#", recovered);
Note: There is no need to wrap your array in a NSMutableDictionary.
When that works change it to the file based method calls.
Check that the filePath is valid.
Check that the file is created.
Check that the file contents are the same as in the above test code.
Note: There is no reason to wrap your array in a NSMutableDictionary.

isMemberOfClass doesn't work as expected with ocunit [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
'isMemberOfClass' returning 'NO' when custom init
I've some trouble with the "isMemberOfClass"-Method.
I have a class, that generates and returns objects ("MyObject")
// ObjectFactory.h
...
-(MyObject*)generateMyObject;
...
// ObjectFactory.m
...
-(MyObject*)generateMyObject
{
MyObject *obj = [[MyObject alloc]init];
obj.name = #"Whatever"; // set properties of object
return obj;
}
...
And there's a unittest-class, that calls the generateMyObject-selector and checks the class of the returned object:
...
ObjectFactory *factory = [[ObjectFactory alloc]init];
MyObject *obj = [factory generateMyObject];
if (![obj isMemeberOfclass:[MyObject class]])
STFail(#"Upps, object of wrong class returned...");
else
...
I expect, that the else-part is processed...but the STFail(...) is called instead, but why?
Thx for any help!
Regards,
matrau
Ok, here is the original copy&pasted code:
//testcase
- (void)test001_setCostumeFirstCostume
{
NSString *xmlString = #"<Bricks.SetCostumeBrick><costumeData reference=\"../../../../../costumeDataList/Common.CostumeData\"/><sprite reference=\"../../../../..\"/></Bricks.SetCostumeBrick>";
NSError *error;
NSData *xmlData = [xmlString dataUsingEncoding:NSASCIIStringEncoding];
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:xmlData
options:0 error:&error];
SetCostumeBrick *newBrick = [self.parser loadSetCostumeBrick:doc.rootElement];
if (![newBrick isMemberOfClass:[SetCostumeBrick class]])
STFail(#"Wrong class-member");
}
// "MyObject"
#implementation SetCostumeBrick
#synthesize indexOfCostumeInArray = _indexOfCostumeInArray;
- (void)performOnSprite:(Sprite *)sprite fromScript:(Script*)script
{
NSLog(#"Performing: %#", self.description);
[sprite performSelectorOnMainThread:#selector(changeCostume:) withObject:self.indexOfCostumeInArray waitUntilDone:true];
}
- (NSString*)description
{
return [NSString stringWithFormat:#"SetCostumeBrick (CostumeIndex: %d)", self.indexOfCostumeInArray.intValue];
}
#end
// superclass of SetCostumeBrick
#implementation Brick
- (NSString*)description
{
return #"Brick (NO SPECIFIC DESCRIPTION GIVEN! OVERRIDE THE DESCRIPTION METHOD!";
}
//abstract method (!!!)
- (void)performOnSprite:(Sprite *)sprite fromScript:(Script*)script
{
#throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:#"You must override %# in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
}
#end
// the "factory" (a xml-parser)
- (SetCostumeBrick*)loadSetCostumeBrick:(GDataXMLElement*)gDataSetCostumeBrick
{
SetCostumeBrick *ret = [[SetCostumeBrick alloc] init];
NSArray *references = [gDataSetCostumeBrick elementsForName:#"costumeData"];
GDataXMLNode *temp = [(GDataXMLElement*)[references objectAtIndex:0]attributeForName:#"reference"];
NSString *referencePath = temp.stringValue;
if ([referencePath length] > 2)
{
if([referencePath hasSuffix:#"]"]) //index found
{
NSString *indexString = [referencePath substringWithRange:NSMakeRange([referencePath length]-2, 1)];
ret.indexOfCostumeInArray = [NSNumber numberWithInt:indexString.intValue-1];
}
else
{
ret.indexOfCostumeInArray = [NSNumber numberWithInt:0];
}
}
else
{
ret.indexOfCostumeInArray = nil;
#throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:#"Parser error! (#1)"]
userInfo:nil];
}
NSLog(#"Index: %#, Reference: %#", ret.indexOfCostumeInArray, [references objectAtIndex:0]);
return ret;
}
SOLUTION:
Eiko/jrturton gave me a link to the solution - thx: isMemberOfClass returns no when ViewController is instantiated from UIStoryboard
The problem was, that the classes were included in both targets (app and test bundle)
Thank you guys for your help :)
You generally want isKindOfClass:, not isMemberOfClass. The isKindOfClass: will return YES if the receiver is a member of a subclass of the class in question, whereas isMemberOfClass: will return NO in the same case.
if ([obj isKindOfClass:[MyObject class]])
For example,
NSArray *array = [NSArray array];
Here [array isMemberOfClass:[NSArray class]] will return NO but [array isKindOfClass:[NSArray class]] will return YES.
Ok, with different class addresses per your comment, I think I can track this down to be a duplicate of this:
isMemberOfClass returns no when ViewController is instantiated from UIStoryboard
Basically, your class is included twice.

Obj-C easy method to convert from NSObject with properties to NSDictionary?

I ran across something that I eventually figured out, but think that there's probably a much more efficient way to accomplish it.
I had an object (an NSObject which adopted the MKAnnotation protocol) that had a number of properties (title, subtitle,latitude,longitude, info, etc.). I needed to be able to pass this object to another object, which wanted to extract info from it using objectForKey methods, as an NSDictionary (because that's what it was getting from another view controller).
What I ended up doing was create a new NSMutableDictionary and use setObject: forKey on it to transfer each piece of vital info, and then I just passed on the newly created dictionary.
Was there an easier way to do this?
Here's the relevant code:
// sender contains a custom map annotation that has extra properties...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetailFromMap"])
{
DetailViewController *dest =[segue destinationViewController];
//make a dictionary from annotaion to pass info
NSMutableDictionary *myValues =[[NSMutableDictionary alloc] init];
//fill with the relevant info
[myValues setObject:[sender title] forKey:#"title"] ;
[myValues setObject:[sender subtitle] forKey:#"subtitle"];
[myValues setObject:[sender info] forKey:#"info"];
[myValues setObject:[sender pic] forKey:#"pic"];
[myValues setObject:[sender latitude] forKey:#"latitude"];
[myValues setObject:[sender longitude] forKey:#"longitude"];
//pass values
dest.curLoc = myValues;
}
}
Thanks in advance for your collective wisdom.
Here's what I came up with, thanks to the folks, below...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"showDetailFromMap"])
{
DetailViewController *dest =[segue destinationViewController];
NSArray *myKeys = [NSArray arrayWithObjects:
#"title",#"subtitle",#"info",#"pic",#"latitude",#"longitude", nil];
//make a dictionary from annotaion to pass info
NSDictionary *myValues =[sender dictionaryWithValuesForKeys:myKeys];
//pass values
dest.curLoc = myValues;
}
}
And a even simpler fix, as seen below...
Using valueForKey instead of object for key to retrieve the information.
Sure thing! Use the objc-runtime and KVC!
#import <objc/runtime.h>
#interface NSDictionary(dictionaryWithObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id) obj;
#end
#implementation NSDictionary(dictionaryWithObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
[dict setObject:[obj valueForKey:key] forKey:key];
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
#end
And you would use like this:
MyObj *obj = [MyObj new];
NSDictionary *dict = [NSDictionary dictionaryWithPropertiesOfObject:obj];
NSLog(#"%#", dict);
This is an old post and Richard J. Ross III's answer is really helpful, but in case of custom objects (an custom class has another custom object in it). However, sometimes properties are other objects and so forth, making the serialization a bit complicated.
Details * details = [[Details alloc] init];
details.tomato = #"Tomato 1";
details.potato = #"Potato 1";
details.mangoCount = [NSNumber numberWithInt:12];
Person * person = [[Person alloc]init];
person.name = #"HS";
person.age = #"126 Years";
person.gender = #"?";
person.details = details;
For converting these type of objects (multiple custom objects) into dictionary, I had to modify Richard J. Ross III's Answer a little bit.
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
Class classObject = NSClassFromString([key capitalizedString]);
if (classObject) {
id subObj = [self dictionaryWithPropertiesOfObject:[obj valueForKey:key]];
[dict setObject:subObj forKey:key];
}
else
{
id value = [obj valueForKey:key];
if(value) [dict setObject:value forKey:key];
}
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
I hope it will help someone. Full credit goes to Richard J. Ross III.
If the properties had the same names as the keys used to access the dictionary then you could have just used KVC and had valueForKey: instead of objectForKey.
For example given this dictionary
NSDictionary *annotation = [[NSDictionary alloc] initWithObjectsAndKeys:
#"A title", #"title", nil];
and this Object
#interface MyAnnotation : NSObject
#property (nonatomic, copy) NSString *title;
#end
it wouldn't matter if I had an instance of the dictionary or MyAnnotation I could call
[annotation valueForKey:#"title"];
Obviously that works the other way as well e.g.
[annotation setValue:#"A title" forKey:#"title"];
To complete the method of Richard J. Ross, this one works with NSArray of custom object.
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
Class classObject = NSClassFromString([key capitalizedString]);
id object = [obj valueForKey:key];
if (classObject) {
id subObj = [self dictionaryWithPropertiesOfObject:object];
[dict setObject:subObj forKey:key];
}
else if([object isKindOfClass:[NSArray class]])
{
NSMutableArray *subObj = [NSMutableArray array];
for (id o in object) {
[subObj addObject:[self dictionaryWithPropertiesOfObject:o] ];
}
[dict setObject:subObj forKey:key];
}
else
{
if(object) [dict setObject:object forKey:key];
}
}
free(properties);
return [NSDictionary dictionaryWithDictionary:dict];
}
There are so many solutions and nothing worked for me as I had a complex nested object structure. This solution takes things from Richard and Damien but improvises as Damien's solution is tied to naming keys as class names.
Here is the header
#interface NSDictionary (PropertiesOfObject)
+(NSDictionary *) dictionaryWithPropertiesOfObject:(id)obj;
#end
Here is the .m file
#implementation NSDictionary (PropertiesOfObject)
static NSDateFormatter *reverseFormatter;
+ (NSDateFormatter *)getReverseDateFormatter {
if (!reverseFormatter) {
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
reverseFormatter = [[NSDateFormatter alloc] init];
[reverseFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
[reverseFormatter setLocale:locale];
}
return reverseFormatter;
}
+ (NSDictionary *)dictionaryWithPropertiesOfObject:(id)obj {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
unsigned count;
objc_property_t *properties = class_copyPropertyList([obj class], &count);
for (int i = 0; i < count; i++) {
NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
id object = [obj valueForKey:key];
if (object) {
if ([object isKindOfClass:[NSArray class]]) {
NSMutableArray *subObj = [NSMutableArray array];
for (id o in object) {
[subObj addObject:[self dictionaryWithPropertiesOfObject:o]];
}
dict[key] = subObj;
}
else if ([object isKindOfClass:[NSString class]]) {
dict[key] = object;
} else if ([object isKindOfClass:[NSDate class]]) {
dict[key] = [[NSDictionary getReverseDateFormatter] stringFromDate:(NSDate *) object];
} else if ([object isKindOfClass:[NSNumber class]]) {
dict[key] = object;
} else if ([[object class] isSubclassOfClass:[NSObject class]]) {
dict[key] = [self dictionaryWithPropertiesOfObject:object];
}
}
}
return dict;
}
#end
You also can use the NSObject+APObjectMapping category which is available on GitHub: https://github.com/aperechnev/APObjectMapping
It's a quit easy. Just describe the mapping rules in your class:
#import <Foundation/Foundation.h>
#import "NSObject+APObjectMapping.h"
#interface MyCustomClass : NSObject
#property (nonatomic, strong) NSNumber * someNumber;
#property (nonatomic, strong) NSString * someString;
#end
#implementation MyCustomClass
+ (NSMutableDictionary *)objectMapping {
NSMutableDictionary * mapping = [super objectMapping];
if (mapping) {
NSDictionary * objectMapping = #{ #"someNumber": #"some_number",
#"someString": #"some_string" };
}
return mapping
}
#end
And then you can easily map your object to dictionary:
MyCustomClass * myObj = [[MyCustomClass alloc] init];
myObj.someNumber = #1;
myObj.someString = #"some string";
NSDictionary * myDict = [myObj mapToDictionary];
Also you can parse your object from dictionary:
NSDictionary * myDict = #{ #"some_number": #123,
#"some_string": #"some string" };
MyCustomClass * myObj = [[MyCustomClass alloc] initWithDictionary:myDict];

Apparent memory leak in FastEnumeration when deriving from NSMutableDictionary

I have a class that acts as a subclass of NSMutableDictionary (mostly by delegation) as we have some custom interfaces wrapped around a dictionary. When running the ios leak instrument it is identifying my keyEnumerator method as the source of the leak of NSFastEnumerationEnumerator objects.
Here is my keyEnumeration method as a delegate for the wrapped NSMutableDictionary.
- (NSEnumerator*) keyEnumerator {
return [dictionary keyEnumerator];
}
The backtrace for the leak always shows an enumerator as the source:
- (void) someMethod {
for (NSString *key in myWrappedDictionary) { ... }
}
Here is a typical backtrace:
calloc
class_createInstance
__CFAllocateObject2
-[__NSCFDictionary keyEnumerator]
-[WrappedDictionary keyEnumerator]
-[NSDictionary countByEnumerating...
-[SomeClass someMethod]
I'm looking for a workaround or flaw in my one line of code. I'm using ARC.
An example class is shown below. Invoking [WrappedDictionary createLeaks] will create 9 leaks.
#interface WrappedDictionary : NSMutableDictionary {
NSMutableDictionary *dictionary;
}
- (id) init;
- (NSUInteger) count;
- (NSEnumerator*) keyEnumerator;
- (void)setObject:(id)anObject forKey:(id)key;
#end
#implementation WrappedDictionary
- (id) init {
dictionary = [NSMutableDictionary new];
return self;
}
- (NSUInteger) count { return [dictionary count]; }
- (NSEnumerator*) keyEnumerator {
return [dictionary keyEnumerator];
}
- (void)setObject: anObject forKey:key {
[dictionary setObject:anObject forKey: key];
}
+ (void) createLeaks {
for (int i=0; i < 10; i++) {
WrappedDictionary *dict = [WrappedDictionary new];
[dict setObject:#"1" forKey:#"1"];
[dict setObject:#"2" forKey:#"2"];
[dict setObject:#"3" forKey:#"3"];
for (NSString *key in dict) {
NSLog(#"key=%#",key);
}
}
}
#end
Remember, the leaks instrument just shows where a leaked piece of memory is allocated. That doesn't mean that the allocation point is the source of the leak. The more likely source of the leak is in someMethod, or in the caller of someMethod, particularly if you put this into an ivar and then have a retain cycle on the entire object.
I have discovered a simple workaround.
If I change the keyEnumerator method from
- (NSEnumerator*) keyEnumerator {
return [dictionary keyEnumerator];
}
to
- (NSEnumerator*) keyEnumerator {
NSEnumerator *e = [dictionary keyEnumerator];
return e;
}
the leak goes away. This still does not make sense to me but it apparently forces the compiler to ARC properly the return from dictionary.

Analyzer issue: Potential leak of an object allocated on line 25 and stored into 'oneCopy'

I've upgraded Xcode and have been presented with tons of analyzer warnings like this:
Potential leak of an object allocated on line 25 and stored into 'oneCopy'
Can anyone point me in the right direction?
#implementation NSDictionary(DeepMutableCopy)
-(NSMutableDictionary *)mutableDeepCopy
{
NSMutableDictionary *ret = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for (id key in keys)
{
id oneValue = [self valueForKey:key];
id oneCopy = nil;
if ([oneValue respondsToSelector:#selector(mutableDeepCopy)])
oneCopy = [oneValue mutableDeepCopy];
else if ([oneValue respondsToSelector:#selector(mutableCopy)])
oneCopy = [oneValue mutableCopy];
if (oneCopy == nil)
oneCopy = [oneValue copy];
[ret setValue:oneCopy forKey:key];
}
return ret;
}
#end
Screenshot showing lines numbers:
#import "NSDictionary-DeepMutableCopy.h"
#implementation NSDictionary(DeepMutableCopy)
-(NSMutableDictionary *)mutableDeepCopy
{
//NSMutableDictionary *ret = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
NSMutableDictionary *ret = [NSMutableDictionary dictionaryWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for (id key in keys)
{
id oneValue = [self valueForKey:key];
id oneCopy = nil;
if ([oneValue respondsToSelector:#selector(mutableDeepCopy)])
oneCopy = [oneValue mutableDeepCopy];
else if ([oneValue respondsToSelector:#selector(mutableCopy)])
oneCopy = [oneValue mutableCopy];
if (oneCopy == nil)
oneCopy = [oneValue copy];
[ret setValue:oneCopy forKey:key];
[oneCopy release];
}
return ret;
}
#end
Addressing your second issue first, instead of [[NSMutableDictionary alloc] initWithCapacity:[self count]] you could use [NSMutableDictionary dictionaryWithCapacity:[self count]] which will return an autoreleased object and you will have to retain it yourself in the calling code.
On the other hand, you could rename your method to start with the word copy if you want it to return a retained object and not throw errors - which I think is exactly what you want to do in this case. The rest of my response assumes you have taken this path.
My original answer was as follows: You are not releasing oneCopy at the end of each iteration. Try adding [oneCopy release]; right after [ret setValue:oneCopy forKey:key];.
However, as Alexsander Akers points out the compiler thinks that -mutableDeepCopy has a 0 refcount. So, if you rename as suggested above and include [oneCopy release] as I originally suggested it should take care of both issues. If it doesn't, definitely check out some of the other solutions in the question he referenced.
Example:
#implementation NSDictionary(DeepMutableCopy)
-(NSMutableDictionary *)copyWithDeepCopiedValues
{
NSMutableDictionary *ret = [[NSMutableDictionary alloc] initWithCapacity:[self count]];
NSArray *keys = [self allKeys];
for (id key in keys)
{
id oneValue = [self valueForKey:key];
id oneCopy = nil;
if ([oneValue respondsToSelector:#selector(mutableDeepCopy)])
oneCopy = [oneValue copyWithDeepCopiedValues];
else if ([oneValue respondsToSelector:#selector(mutableCopy)])
oneCopy = [oneValue mutableCopy];
if (oneCopy == nil)
oneCopy = [oneValue copy];
[ret setObject:oneCopy forKey:key];
[oneCopy release];
}
return ret;
}
#end
There are two problems here. First, as #David Brainer-Banker says, you need to release oneCopy at the end of each iteration by placing [oneCopy release]; after you set[ret setValue:oneCopy forKey:key];`.
The incorrect reference count decrement is the second problem. This is because the oneCopy object may have a +1 or a 0 reference count. The objects returned by -copy and -mutableCopy have a +1 refcount, but the object returned by -deepMutableCopy has a 0 refcount because it isn't in the new, copy, or create (et al.) families.
This question is an exact duplicate of this one and has some great responses.