I am new to objective C and trying to learn it. I am trying to write calculator program which performs simple mathematical calculation(addition, subtraction and so forth).
I want to create an array which stores for numbers(double value) and operands. Now, my pushOperand method takes ID as below:
-(void) pushOperand:(id)operand
{
[self.inputStack addObject:operand];
}
when I try to push double value as below:
- (IBAction)enterPressed:(UIButton *)sender
{
[self.brain pushOperand:[self.displayResult.text doubleValue]];
}
It gives my following error: "Sending 'double' to parameter of incompatible type 'id'"
I would appreciate if you guys can answer my following questions:
'id' is a generic type so I would assume it will work with any type without giving error above. Can you please help me understand the real reason behind the error?
How can I resolve this error?
id is a pointer to any class. Hence, it does not work with primitive types such as double or int which are neither pointers, nor objects. To store a primitive type in an NSArray, one must first wrap the primitive in an NSNumber object. This can be done in using alloc/init or with the new style object creation, as shown in the two snippets below.
old style
NSNumber *number = [[NSNumber alloc] initWithDouble:[self.displayResult.text doubleValue]];
[self.brain pushOperand:number];
new style
NSNumber *number = #( [self.displayResult.text doubleValue] );
[self.brain pushOperand:number];
I suggest using it with an NSNumber: Try not to abuse using id where you don't need to; lots of issues can arise if not.
- (void)pushOperand:(NSNumber *)operand
{
[self.inputStack addObject:operand];
}
- (IBAction)enterPressed:(UIButton *)sender
{
[self.brain pushOperand:#([self.displayResult.text doubleValue])];
}
Related
What is the difference between these two lines?
alertObj.AlertAddressed=[[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] boolValue];
alertObj.AlertAddressed=(BOOL)[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)];
I'm getting a different result for these two lines - why?
Thanks in advance.
First one gives you actual bool value.
Second one type cast to BOOL the result of
[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)]
I don't know how good you are at pointers but I try to explain
First of all when you get an NSNumber it is an object, and the value of an object is at first it's pointer (so something like 0x0000af) this is simple an adress in the memory, and this address contains your NSNUmber wich contains the actual value (so let's say a bool information in your example)
It's pretty simple, when you do
(BOOL)alertObj.AlertAddressed=(BOOL)[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)];
what actually happen is the try to cast the 0x0000af part to a bool value... NOT what NSNumber contains (what you actually get depends on the current Pointer of the object when you try this)
alertObj.AlertAddressed=[[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] boolValue];
this is something actually implemented in NSNumber and therefore it completly respects what the object does and gives you the bool saved in your NSNumber, and not a cast from it's pointer
NSNumber is an object, BOOL is a primitive type.
NSNumber is a class that wraps numbers, but you can't use to make operations, if you want you should unwrap calling a specific method. Since NSNumber is an object your variable is a pointer holding a reference to an object, not a value.
The first line is correct, the second is wrong because you are casting a pointer to a bool type.
alertObj.AlertAddressed=[[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] boolValue]; This will convert you value to BOOL
alertObj.AlertAddressed=(BOOL)[NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)];This is like casting. Considers [NSNumber numberWithBool:sqlite3_column_int(compiledStatement, 9)] will return bool value.
This question already has answers here:
Should you use 'isEqual' or '=='?
(2 answers)
Comparing objects in Obj-C
(4 answers)
Closed 8 years ago.
I am reading the Programming with Objective-C . In the section of Determining Equality of Objects , it says the following words:
- When dealing with objects, the == operator is used to test whether two separate pointers are pointing to the same object:
if (firstPerson == secondPerson) {
// firstPerson is the same object as secondPerson
}
- If you need to test whether two objects represent the same data, you need to call a method like isEqual:, available from NSObject:
if ([firstPerson isEqual:secondPerson]) {
// firstPerson is identical to secondPerson
}
I get confused about the differences between == and isEqual with the above explanation, does it mean firstPerson == secondPerson is an alternative of [firstPerson isEqual:secondPerson] ?
The definition of == is correct, it checks to see that they're pointing to the actual same pointer/memory address (ie. 0xffffff)
The key to understanding what you're asking is to think about what you mean by the word "equal". "equal" typically means, from the developer's point of view, that "these two objects contain the same data in the fields that I require for all practical purposes". You can have two user objects each with the same userID property but different times in lastUpdated - would you consider them equal? Depends on your use case. Most likely you would say yes because they're the same user. They were updated from the server at different times, so some fields differ, but for your implementation, they're equal.
In the case above, are they the same object? Definitely not. They point to different memory addresses. So == would be NO, whereas if you wrote your isEqual: method to check just the userID property, it would return YES
The definition of isEqual: is entirely up to the author of the class. isEqual: can be written to use == if you wanted. All you have to do, in your class, is to override the isEqual: method which is defined by the NSObject protocol.
If you have a custom object, use isEqual: to define what your definition of equal is. In the example of a user object, you might define:
- (BOOL)isEqual:(id)otherObject {
if ([otherObject isKindOfClass:[self class]]) {
MyClass *otherObjectAfterCast = (MyClass*)otherObject;
if ([otherObjectAfterCast.userID isEqualToString:self.userID])
return YES;
}
return NO;
}
Technically you'd probably want to use caseInsensitiveCompare: or something like that but you get the drift...
isEqual: can also be used to trigger other methods - in the case of NSString - calling isEqual: when both operands are strings results in a call to isEqualToString: - which is why the documentation recommends calling isEqualToString: if you know they're both strings, since it's a bit faster.
So, isEqual: is whatever you make of it, or whatever the class author has defined it to be.
This is also a pretty clear definition in the docs (for once lol): NSObject Protocol Reference
Hope this helps! Let me know if you need any further clarification.
NSString *string1 = [[NSString alloc] initWithString:#"some string"];
NSString *string2 = [[NSString alloc] initWithString:#"some string"];
NSString *string3 = string2;
BOOL equal1 = (string1 == string2); // NO
BOOL equal2 = [string1 isEqual:string2]; // YES
BOOL equal3 = (string2 == string3); // YES
BOOL equal4 = [string2 isEqualToString:string3]; // YES
The simple version is this.
== tells you if the pointers are the same object or not.
The isEqual: family of methods do something different.
They tell you if the objects at the other end of the pointers are effectively the same based on some criteria such as the properties or ivars holding equal values or whatever logic is implemented in the method used. They may or may not be the exact same object.
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!
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.
My brain is failing me today. I know this has got to be a simple one, but I just don't see it.
CGFloat *minutes = [self.displayData objectForKey:index];
Incompatible integer to pointer conversion sending 'NSUInteger' (aka 'unsigned int') to parameter of type 'id'
index is a NSUInteger in a loop, (0 1 2 3 etc)
How do I get past this one? Thanks.
The dictionary waits for an object as the key (id) rather than a plain integer (NSUInteger).
Try wrapping the integer in a NSNumber object.
CGFloat *minutes = [self.displayData objectForKey:[NSNumber numberWithUnsignedInteger:index]];
The -objectForKey: method returns a value of type id, but you're trying to assign that to a CGFloat*, which isn't compatible with id because CGFloat isn't a class. If you know that the object you're retrieving from the dictionary is a certain type, say an NSNumber, you can take steps to convert it to a CGFloat when you get it.
Also, you're trying to use an int as a key into a dictionary, but dictionaries require their keys to be objects too. If you want to access your objects by index, store them in an array instead of a dictionary.
Putting it all together, you'd have something like this:
// after changing the displayData to an array
NSNumber *number = [self.displayData objectAtIndex:index];
CGFloat minutes = [number floatValue];
You should not be using a pointer like this:
CGFloat minutes = [self.displayData objectForKey:index];
Easy question. Just don't use NSUIntegrer. Instead of it, do the following:
id index;//then set the index
I am using below code, it gives the error:
incompatible pointer types assignment to 'NSString *'
to the parament of the 'NSString *'" where dataParameter is used.
Here is the code:
- (id)initWithText:(NSString *)someText passedData:(animalsViewController *)dataParameter
{
NSLog(dataParameter);
self.title=dataParameter;
nowCountry=dataParameter;
return self;
}
How can I fix this?
You're treating "dataParameter", which is an instance of "animalsViewController" as an NSString. That doesn't work, you can only assign objects of the same type to each other.
NSLog is expecting a string parameter. Use the formatting arguments to convert dataParameter to an NSString:
NSLog(#"%#", dataParameter);
For your other two assignments, it seems unlikely that you want to assign a viewController object to a property like title that is an NSString. It's more likely that you want to assign an NSString property of dataParameter, something like this:
self.title = dataParameter.title;
or:
nowCountry = dataParameter.country;
Since I don't know what properties an AnimalsViewController has, I'm just guessing a property names, but that should illustrate the point.