Problems on NSArray's -valueForKey: when its item is NSDictionary - objective-c

I have an array which contains items of NSDictionary, I want to transform the items to other objects, my first thought is valueForKey:, so I add a category method toMyObject for NSDictionary, and call for:
[array valueForKey:#"toMyObject"]
But it doesn't work as expect, it just returns the array of NSNulls.
Any ideas to solve this problem if I don't want to enumerate the array?

Answer to myself. The valueForKey: of dictionary overwrite the default behavior, if the dictionary doesn't have the key, it will return nil and not call the accessor method as NSObject do, as Apple document says:
If key does not start with “#”, invokes objectForKey:. If key does
start with “#”, strips the “#” and invokes [super valueForKey:] with
the rest of the key.
Since NSDictionary is a cluster class, it's not recommend to subclass to overwrite the behavior. Instead I use the method swiss like this:
#implementation NSDictionary (MyAddition)
static void swizzle(Class c, SEL orig, SEL new)
{
Method origMethod = class_getInstanceMethod(c, orig);
Method newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
+ (void)initialize
{
if (self == [NSDictionary class]){
swizzle([NSDictionary class],
#selector(valueForKey:),
#selector(myValueForKey:));
}
}
- (id)toMyObject
{
return toMyObject;
}
...
- (id)myValueForKey:(NSString *)key
{
// for collection operators
if ([key compare:#"#" options:0 range:NSMakeRange(0, 1)] == NSOrderedSame)
return [super valueForKey:key];
if ([key isEqualToString:#"toMyObject"])
return [self toMyObject];
return [self myValueForKey:key];
}
Now it's safe for an NSArray to call valueForKey:#"toMyObject".

One more implementation without swizzling:
#implementation NSObject (MLWValueForKey)
- (id)mlw_valueForKey:(NSString *)key {
if ([key hasPrefix:#"#"]) {
return [self valueForKey:key];
}
NSAssert(![key containsString:#":"], #"Key should be selector without arguments");
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [self performSelector:NSSelectorFromString(key)];
#pragma clang diagnostic pop
}
#end
#implementation NSArray (MLWValueForKey)
- (id)mlw_valueForKey:(NSString *)key {
if ([key hasPrefix:#"#"]) {
return [self valueForKey:key];
}
NSMutableArray *array = [NSMutableArray arrayWithCapacity:self.count];
for (id object in self) {
[array addObject:[object mlw_valueForKey:key]];
}
return array;
}
#end

Related

Duplicated custom object in NSSet

I have some problems about the NSMutableSet in Objective-C.
I learnt that the NSSet will compare the two objects' hash code to decide whether they are identical or not.
The problems is, I implemented a class that is subclass of NSObject myself. There is a property NSString *name in that class. What I want to do is when instances of this custom class has the same variable value of "name" , they should be identical, and such identical class should not be duplicated when adding to an NSMutableSet.
So I override the - (NSUInteger)hash function, and the debug shows it returns the same hash for my two instances obj1, obj2 (obj1.name == obj2.name). But when I added obj1, obj2 to an NSMutableSet, the NSMutableSet still contained both obj1, obj2 in it.
I tried two NSString which has the same value, then added them to NSMutableSet, the set will only be one NSString there.
What could be the solution? Thank you for any help!
The custom Class:
Object.h:
#import <Foundation/Foundation.h>
#interface Object : NSObject
#property (retain) NSString *name;
#end
Object.m
#implementation Object
#synthesize name;
-(BOOL)isEqualTo:(id)obj {
return [self.name isEqualToString:[(Object *)obj name]] ? true : false;
}
- (NSUInteger)hash {
return [[self name] hash];
}
#end
and main:
#import <Foundation/Foundation.h>
#import "Object.h"
int main(int argc, const char * argv[])
{
#autoreleasepool {
Object *obj1 = [[Object alloc]init];
Object *obj2 = [[Object alloc]init];
obj1.name = #"test";
obj2.name = #"test";
NSMutableSet *set = [[NSMutableSet alloc] initWithObjects:obj1, obj2, nil];
NSLog(#"%d", [obj1 isEqualTo:obj2]);
NSLog(#"%ld", [set count]);
}
return 0;
}
Instead of implementing isEqualTo: you have to implement isEqual:
- (BOOL)isEqual:(id)object {
return [object isKindOfClass:[MyObject class]] &&
[self.name isEqual:[(MyObject *)object name]];
}
This will (probably falsely) return NO if both self.name and object.name are nil. If you want to return YES if both properties are nil you should use
- (BOOL)isEqual:(id)object {
if ([object isKindOfClass:[MyObject class]]) {
return (!self.name && ![(MyObject *)object name]) ||
[self.name isEqual:[(MyObject *)object name]];
}
return NO;
}

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.

NSCode: encoder and decoder for primitive types

I was trying to create a generic encoder and decoder for my model classes. I was trying to find a way to call the "encode method" for all types of properties, either objects (NSString, NSNumber, NSArray, etc...) and primitive types. And I saw someone doing the following. And I was wondering if this would a correct way to do it.
Properties:
#property (assign,nonatomic) int integerP;
#property (assign,nonatomic) float floatP;
#property (assign,nonatomic) BOOL boolP;
Enconder and Decoder Code:
- (void)encodeWithCoder:(NSCoder *)encoder
{
id object2 = [self valueForKey:#"integerP"];
id object3 = [self valueForKey:#"floatP"];
id object4 = [self valueForKey:#"boolP"];
[encoder encodeObject:object2 forKey:#"integerP"];
[encoder encodeObject:object3 forKey:#"floatP"];
[encoder encodeObject:object4 forKey:#"boolP"];
//[self setValue:[NSNumber numberWithInt:90] forKey:#"heightR"];
//NSLog(#"%#",[self valueForKey:#"heightR"]);
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if( self != nil )
{
id object2 = [decoder decodeObjectForKey:#"integerP"];
[self setValue:object2 forKey:#"integerP"];
id object3 = [decoder decodeObjectForKey:#"floatP"];
[self setValue:object3 forKey:#"floatP"];
id object4 = [decoder decodeObjectForKey:#"boolP"];
[self setValue:object4 forKey:#"boolP"];
}
return self;
}
I was not sure if this is a correct way, or if other program or object could write in the same memory space of the primitive properties. If the method above is correct, what is the difference between the above and this:
The way I thought was correct:
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeInt:integerP forKey:#"integerP"];
[encoder encodeFloat:floatP forKey:#"floatP"];
[encoder encodeBool:boolP forKey:#"boolP"];
//[self setValue:[NSNumber numberWithInt:90] forKey:#"heightR"];
//NSLog(#"%#",[self valueForKey:#"heightR"]);
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if( self != nil )
{
integerP = [decoder decodeIntForKey:#"integerP"];
floatP = [decoder decodeFloatForKey:#"floatP"];
boolP = [decoder decodeBoolForKey:#"boolP"];
}
return self;
}
I tested and both methods returned the correct values.
Both methods will work.
The first is particularly clever, being that valueForKey: will always return an NSObject, even when the value is actually a primitive, so float/int/bool types will be wrapped in an NSNumber automatically by the KVC getter, and unwrapped in the KVC setter.
It might be possible to use this to implement some sort of generic encode/decode functions that operate on an array of property keys.
However, the second example is the standard way to do it, and the way I'd probably recommend. Sometimes you've got to write boilerplate code!
Try this:
BaseModel.h
#interface BaseModel : NSObject<NSCoding>
#end
BaseModel.m
- (NSArray *)keysForEncoding
{
[NSException raise:#"keysForEncoding" format:#"keysForEncoding must be implemented in child class!"];
//example implementation in child class:
//return #[#"airtime", #"channelID", #"duration", #"programID", #"shortTitle"];
return nil;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
for(NSString* key in [self keysForEncoding])
{
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
for (NSString* key in [self keysForEncoding]) {
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
}
return self;
}
Then derive from that base class your class with actual data.
Example of simple class:
EPGData.h
#interface EPGData : BaseModel
#property(nonatomic, assign) NSTimeInterval airtime; //date in 1970 format
#property(nonatomic, assign) int channelID;
#property(nonatomic, assign) float duration; //in seconds
#property(nonatomic, assign) unsigned int programID;
#property(nonatomic, strong) NSString* shortTitle;
#end
EPGData.m
- (NSArray *)keysForEncoding;
{
return [NSArray arrayWithObjects:#"airtime", #"channelID", #"duration", #"programID", #"shortTitle", nil];
}
You first method looks very strange!
In Objective-C float/int/BOOL are not "Object". You can convert them into a NSNumber by calling:
NSNumber *aNumber = [NSNumber numberWithInt:integerP];
But your second solution looks fine.
Only use decodeObjectForKey for Objects like NSArray, etc. or your own class (where you also need to add the coding/decoding methods!)
And leave your fingers from setValue and valueForKey.

Class methods, saving state, a better way to register functions

I'm working on an assignment which involves making an RPN calculator. I am currently using class methods to check whether a string is an operation as below:
+ (NSSet *) noOpOperations {
return [NSSet setWithObjects:#"π", nil];
}
+ (NSSet *) unaryOperations {
return [NSSet setWithObjects:#"sin",#"cos",#"log",#"+/-", nil];
}
+ (NSSet *) binaryOperations {
return [NSSet setWithObjects:#"+",#"-",#"*",#"/", nil];
}
+ (NSSet *) operations {
/* surely there is a better way - is it possible to save this call and reuse it? Or what about having the objects register themselves so you can add operations more easily? */
return [[self noOpOperations] setByAddingObjectsFromSet:
[[self unaryOperations] setByAddingObjectsFromSet:
[self binaryOperations]]];
}
+ (BOOL) isOperation:operand {
return [[self operations] containsObject:operand];
}
I believe it would be better to implement a kind of function registry system to allow dynamic adding of operations from another location in the project, but I think it would require a class variable. Is there a better way to do this than how I am doing it now?
My Personal solution for situations like this:
#define INT_OBJ(x) [NSNumber numberWithInt:x]
#implementation MyClass
static NSDictionary *operations;
enum {
kOperationNoOp = 1,
kOperationUnaryOp,
kOperationBinaryOp,
};
+(void) initialize
{
operations = [NSDictionary dictionaryWithObjectsAndKeys:#"π", INT_OBJ(kOperationNoOp),
// unary operations
#"sin", INT_OBJ(kOperationUnaryOp),
#"cos", INT_OBJ(kOperationUnaryOp),
#"log", INT_OBJ(kOperationUnaryOp),
#"+/-", INT_OBJ(kOperationUnaryOp),
// binary operations
#"+", INT_OBJ(kOperationBinaryOp),
#"-", INT_OBJ(kOperationBinaryOp),
#"*", INT_OBJ(kOperationBinaryOp),
#"/", INT_OBJ(kOperationBinaryOp), nil];
}
-(BOOL) isNoOpOperation:(NSString *) arg
{
return [[operations objectForKey:arg] intValue] == kOperationNoOp;
}
-(BOOL) isUnaryOperation:(NSString *) arg
{
return [[operations objectForKey:arg] intValue] == kOperationUnaryOp;
}
-(BOOL) isBinaryOperation:(NSString *) arg
{
return [[operations objectForKey:arg] intValue] == kOperationBinaryOp;
}
-(BOOL) isAnOperation:(NSString *) arg
{
// if objectForKey: returns nil, intValue will return 0, telling us that the input is not an operation
return [[operations objectForKey:arg] intValue] != 0;
}
#end
I find it to be very straightforward and easy to extend.

deep mutable copy of a NSMutableDictionary

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.