-1

My code is

//    NSDecimalNumber *percentSpent = [distributionModel.spent decimalNumberByDividingBy:self.monthlySummaryModel.totalSpent];
//    NSLog(@"percent:%@", percentSpent);
    NSLog(@"spent:%@, totalSpent:%@", distributionModel.spent, self.monthlySummaryModel.totalSpent);

Where

@property (nonatomic, strong) NSDecimalNumber *spent;
@property (nonatomic) NSDecimalNumber *totalSpent;

Console log is this when the percentSpent is commented out

2014-12-01 15:38:20.161 app-ios[15980:60b] spent:27.01, totalSpent:2077.01 
2014-12-01 15:38:20.201 app-ios[15980:60b] spent:2000, totalSpent:2077.01 
2014-12-01 15:38:20.251 app-ios[15980:60b] spent:40, totalSpent:2077.01 
2014-12-01 15:38:20.292 app-ios[15980:60b] spent:10, totalSpent:2077.01 

Error is this when I execute to calculate percentSpent

2014-12-01 15:35:44.843 app-ios[15963:60b] -[__NSCFNumber decimalNumberByDividingBy:]: unrecognized selector sent to instance 0x17d4f020 
2014-12-01 15:35:44.851 app-ios[15963:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber decimalNumberByDividingBy:]: unrecognized selector sent to instance 0x17d4f020' 
*** First throw call stack: 
(0x2eaadf83 0x3925eccf 0x2eab1917 0x2eab0203 0x2e9ff768 0xac64b 0x313fd8f7 0x313a4c27 0x313a447d 0x312cad59 0x30f4862b 0x30f43e3b 0x30f43ccd 0x30f436df 0x30f434ef 0x30f3d21d 0x2ea79255 0x2ea76bf9 0x2ea76f3b 0x2e9e1ebf 0x2e9e1ca3 0x338e7663 0x3132e14d 0xa48cd 0x3976bab7) 
libc++abi.dylib: terminating with uncaught exception of type NSException 
Signal: 6 (signal SIGABRT)

UPDATE

@interface MonthlySummaryModel : NSObject
@property (nonatomic) int year;
@property (nonatomic) int month;
@property (nonatomic) NSDecimalNumber *totalSpent;
@property (nonatomic, strong) NSArray *distributions;

and

@interface MonthlySummaryDistributionModel : NSObject
@property (nonatomic, strong) NSString *group;
@property (nonatomic, strong) NSDecimalNumber *spent;

- (MonthlySummaryDistributionModel *) initWithJson: (NSDictionary *) json;
@end

What is the issue here?

daydreamer
  • 87,243
  • 191
  • 450
  • 722

1 Answers1

3

A Short and Delightful Essay on What Causes The Dreaded "Unrecognized Selector" Crash

You have crashed because you have sent an NSDecimalNumber message to something that is not an NSDecimalNumber - it is an NSNumber. You may think it is an NSDecimalNumber, and clearly you do think that, as your comment shows:

but I made spent as NSDecimalNumber

... but you are wrong. Believe the runtime, not your gut. The runtime knows much better than you do (clearly) what sort of object this is!

Why is that possible? How did you ever get yourself into this situation? I will explain.

Objective-C is polymorphic. That means it doesn't matter how you declare a thing; what matters is what it is. And they can be different! The reason for that is you can turn off type checking (accidentally) and thus assign an object of totally the wrong type to any variable.

For example (hold my beer and watch this):

NSString* s;
NSNumber* n = [NSNumber new];
NSArray* arr = @[n];
s = arr[0];

Think about what I've just done. I put an NSNumber into an array, pulled it out again, and assigned it to an NSString variable! And the compiler didn't stop me!!!!! That's because pulling something out of an NSArray turns off the compiler's type-checking on that thing - you can assign it to anything, because the compiler has no idea what type of thing it really is.

But it still has a type in real life. So, when the program runs, and when you send an NSString message to s after that, you will crash, because it is not an NSString, despite the declaration.

That was an example of accidentally hiding something's type from the compiler. Sometimes, people do something even worse; they deliberately hide something's type from the compiler. For example (hold my beer again):

NSString* s;
NSNumber* n = [NSNumber new];
s = (NSString*) n;

Whoa! That time I just straight out lied to the compiler and told it that an NSNumber was an NSString!!!!! And the compiler shrugged and said: "OK, whatever. I guess he knows something I don't know." So it let me do that, and it let me assign an NSNumber to an NSString variable.

But once again, it is still an NSNumber. So when we try to treat it as an NSString, we will crash at runtime.

The moral of that story is: don't lie to the compiler! Let it be the judge of what kind of thing a thing is, wherever possible.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • That sort of thing is _such_ a good reason to switch to Swift, which is expressly designed to prevent that very kind of thing from happening. – matt Dec 02 '14 at 00:21
  • Except that this sort of thing is exceedingly rare (and easily diagnosed when it does occur) if one is a reasonably methodical programmer. You mainly see it when newbies believe that assigning/casting to an object type actually changes the object to that type. – Hot Licks Dec 02 '14 at 00:40
  • @HotLicks You raise a good point - I'll add that to my answer. – matt Dec 02 '14 at 00:46