2

Can collection operators be used on primitive values?

I have an object that has a primitive property duration.

@property (nonatomic) NSTimeInterval duration;

I have an NSArray of those said objects and I would like to use a collection operation on the array to get the sum of the durations. The problem is that @"@sum.duration" expects an NSNumber instead.

Will I have to do this the old fashioned way, or is there a way to use primitives?

Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
RileyE
  • 10,874
  • 13
  • 63
  • 106

2 Answers2

4

From "Scalar and Structure Support" in the "Key-Value Coding Programming Guide":

Key-value coding provides support for scalar values and data structures by automatically wrapping and unwrapping NSNumber and NSValue instance values.

So

NSNumber *sum = [array valueForKeyPath:@"@sum.duration"];

just works, even if duration is a scalar property. Small self-contained example:

#import <Foundation/Foundation.h>

@interface MyClass : NSObject
@property(nonatomic, assign) NSTimeInterval duration;
@end

@implementation MyClass
@end

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        MyClass *obj1 = [MyClass new];
        obj1.duration = 123.4;
        MyClass *obj2 = [MyClass new];
        obj2.duration = 456.7;
        NSArray *array = @[obj1, obj2];

        NSNumber *sum = [array valueForKeyPath:@"@sum.duration"];
        NSLog(@"sum = %@", sum);
    }
    return 0;
}

Output: 580.1.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Interesting. I wonder why I received this error: `Exception: [<__NSArrayM 0xe733040> valueForUndefinedKey:]: this class is not key value coding-compliant for the key sum.duration.` – RileyE Dec 04 '13 at 18:25
  • @RileyE: I have tested it before posting the answer. Did you write `@sum.duration` or `sum.duration`? – Martin R Dec 04 '13 at 18:26
  • @RileyE: I have added a working example to my answer. – Martin R Dec 04 '13 at 18:29
  • 2
    @RileyE, are you trying to use `valueForKey` rather than `valueForKeyPath:`? – Rob Napier Dec 04 '13 at 18:30
  • @RobNapier Good catch! I was going off [this post](http://stackoverflow.com/questions/5251255/getting-the-sum-of-all-instances-of-entity-b-from-a-given-instance-of-entity-a) for some odd reason instead of [the documentation](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/KeyValueCoding/Articles/CollectionOperators.html#//apple_ref/doc/uid/20002176-BAJEAIEE). – RileyE Dec 04 '13 at 18:33
  • I'm sorry for any earlier inconvenience, Martin! And thank you! – RileyE Dec 04 '13 at 18:35
0

You can always add another property:

@property (nonatomic) NSNumber *durationNumber;

and implement both the getter and setter:

- (NSNumber *)durationNumber {
    return [NSNumber numberWithDouble:self.duration];
}

- (void)setDurationNumber:(NSNumber *)durationNumber {
    self.duration = durationNumber.doubleValue;
}

Implementing both the setter and getter inhibits automatic synthesis of the backing ivar.

godel9
  • 7,340
  • 1
  • 33
  • 53
  • Yeah. I thought of that, but doing it the old fashioned way seems to be a more optimal way of doing it. A quick for loop iterator that runs once and no need for `NSNumber` allocation. However, thank you for the answer! – RileyE Dec 04 '13 at 18:18