Pregunta

I have an app whose purpose is to compare chronologically ordered time intervals, which are stored by Core Data (via MagicalRecord) as attributes of type double, on an entity called TimedActivity. Ordering directions are supplied by attributes called activityOfInterest and benchmarkActivity on another entity named SearchSpecs. The scheme may seem a bit overcomplicated since I'm pretty green, but that part of it works.

The problem is that getting percentages from two doubles appears to be a bit of a runaround, at least according to the research I've done. I don't need extreme precision. Round seconds are fine. I found a suggestion relating to the use of NSDecimalNumber, but it too seemed like a long way around the corner.

Here is the relevant code in it's current state, with pseudocode to indicate my problem area:

#pragma mark - Go button case handlers

-(void) handleAvsAAction
{
//    NSArray *searchSpecsObjects = [SearchSpecs MR_findAll];
//    NSLog(@"SearchSpecs number of objects is %i",[searchSpecsObjects count]);

    NSArray *searchSpecsArray = [SearchSpecs MR_findAll];
    NSLog(@"searchSpecsArray count is %i", [searchSpecsArray count]);

    SearchSpecs *thisSpec = [searchSpecsArray objectAtIndex:0];
    NSLog(@"SearchSpec activityOfInterest should be %@", thisSpec.activityOfInterest);
    NSLog(@"SearchSpec benchmarkActivity should be %@", thisSpec.benchmarkActivity);

    //    NSArray *activityOfInterestArray;
//    NSArray *benchmarkActivityArray;
    NSNumber *activityOfInterestDurationTotal;
    NSNumber *benchmarkActivityDurationTotal;

    NSPredicate *activityOfInterestPredicate = [NSPredicate predicateWithFormat:@"name == '%@'",thisSpec.activityOfInterest];

    NSPredicate *benchmarkActivityPredicate = [NSPredicate predicateWithFormat:@"name == '%@'", thisSpec.benchmarkActivity];

    activityOfInterestDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:activityOfInterestPredicate];
    NSLog(@"The sum of all the durations for the activity of interest is %zd", activityOfInterestDurationTotal);

    benchmarkActivityDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:benchmarkActivityPredicate];
    NSLog(@"The sum of all the durations for the benchmark activity is %zd", benchmarkActivityDurationTotal);

    [self doTheMathAvsA];

}

-(void) doTheMathAvsA
{
    // Get the total and respective percentages of the totalled durations from the criteria distilled in handleAvsAAction

NSNumber *total;
total = (activityOfInterestDurationTotal + benchmarkActivityDurationTotal);

}

Edit: modified doTheMathAvsA to clarify the desired result.

All help or suggestions appreciated!

Second edit:

OK, your answer below makes sense, and thanks @Martin R!

However, the two quantities in question here originate as NSTimeIntervals, and as mentioned above, are stored as attributes of type double, on an entity called TimedActivity.

So, it seemed rational to me to slightly rewrite the code to extract them from the persistent store as NSTimeIntervals, which I am assured are really just doubles. However, when I do this, I get this error:

Assigning to 'NSTimeInterval' (aka 'double') from incompatible type 'NSNumber *'

Here are the modified declarations:

NSTimeInterval activityOfInterestDurationTotal;
NSTimeInterval benchmarkActivityDurationTotal;

And here's where the error appears:

activityOfInterestDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:activityOfInterestPredicate];
NSLog(@"The sum of all the durations for the activity of interest is %@", activityOfInterestDurationTotal);

benchmarkActivityDurationTotal = [TimedActivity MR_aggregateOperation:@"sum:" onAttribute:@"duration" withPredicate:benchmarkActivityPredicate];
NSLog(@"The sum of all the durations for the benchmark activity is %@", benchmarkActivityDurationTotal);

OK, I assume that the NSNumber referred to in the error message is this property in the TimedActivity managed object subclass, auto-generated from the data model:

@property (nonatomic, retain) NSNumber * duration;

So my question becomes:

Is it really necessary to resort to such seemingly ever-widening circles of conversion and retro-conversion to perform such a seemingly simple calculation? Or am I missing a more straightforward solution?

Thanks!

¿Fue útil?

Solución

You cannot perform arithmetic directly on NSNumber objects. The easiest solution is to convert them to double for the addition:

double tmp = [activityOfInterestDurationTotal doubleValue] + [benchmarkActivityDurationTotal doubleValue];

and the result back to NSNumber, if necessary:

NSNumber *total = @(tmp);

Update: By default, the Xcode generated accessor methods use Objective-C objects even for primitive Core Data types such as "Double". You can change that by selecting the "Use scalar properties for primitive data types" option when creating the subclass files. Then a "Double" property is declared as

@property (nonatomic) double activityOfInterestDurationTotal;

and you can access it "directly" as for example

NSTimeInterval duration = thisActivity.duration;

because NSTimeInterval is just another name for double.

But there is another problem: The MagicalRecord "convenience method" MR_aggregateOperation: uses a special fetch request with NSDictionaryResultType to fetch the sum of all duration values. And even if you chose to use a scalar property for the duration, the result of MR_aggregateOperation: is always some Objective-C object, in this case NSNumber, there is no way around it.

So the only way to avoid a conversion between NSNumber and double would be to use a scalar property as described above, and instead of using MR_aggregateOperation:, fetch all objects and add the duration values yourself in a simple loop.

But note that the fetch request with the aggregate operation performs the calculations on the SQLite level, this is probably more effective then actually fetching all objects.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top