Comparing objects using the compare method in objective-c - objective-c

If I were to try and compare two objects in objective-c, I believe as of IOS 6 it is possible to just use normal operators such as == (proof below), however, I would like to know how this would be done with a -compare method.
As far as I know, the compare method works something in this fashion:
if ([objOne compare:objTwo] == NSOrderedAscending){
// objOne is greater
First of all, is that correct? And secondly, what are the other comparison methods, I.e, NSOrderedAscending?

the writer of a class is resposible for comparing, meaning you have to define when two objects are equal.
This is done by defining a method compare:, that should return either NSOrderedAscending, NSOrderedDescending, NSOrderedSame
From the docs
NSComparisonResult
These constants are used to indicate how items in a request are
ordered.
enum {
NSOrderedAscending = -1,
NSOrderedSame,
NSOrderedDescending
};
typedef NSInteger NSComparisonResult;
Note, that nothing stops you from creating other methods that return those NSComparisionResults. iE NSString has compare: and caseInsensitiveCompare:
== will just check for identity: if the pointers are the same, this will return true, while if there are to different objects but that are the same in mening of the same properties, it will still return false.
From NSComparisonMethods Protocol Reference
The default implementation provided for many of these methods by NSObject is appropriate for objects that implement a single comparison method whose selector, signature, and description match the following:
- (NSComparisonResult)compare:(id)object;
This method should return NSOrderedAscending if the receiver is less
than object, NSOrderedDescending if the receiver is greater than
object, and NSOrderedSame if the receiver and object are equal. For
example, NSString does not implement most of the methods declared in
this informal protocol, but NSString objects still handle messages
conforming to this protocol properly because NSString implements a
compare: method that meets the necessary requirements. Cocoa also
includes appropriate compare: method implementations for the NSDate,
NSDecimalNumber, and NSValue classes.
This means that if you provided a -compare: method, you can also use -isEqual:.
if ([objOne isEqual:objTwo]){ //…

Related

different values for NSNumber long in Objective-C

I have a variable which is a NSNumber of type long. It should hold the value -1.
When I log it to the Console it shows the expected value -1.
NSLog(#"myVariable %#", self.myVariable);
But the following expression in the if-clause is false.
if (myVariable == [NSNumber numberWithInt:-1]) {
...
}
The debugger shows the value 72057594037927935. Does anybody know what's wrong with it?
Thanks!
When you compare NSNumber to other objects, you have two options that do different things:
You can use == to check if two objects represent the same exact object instance, or
You can use isEqual: method to check if two objects represent the same value.
In your case the safest approach would be to use the second alternative
if ([myVariable isEqual:[NSNumber numberWithLong:-1]]) {
...
}
The == approach may or may not work, depending on the way in which you produced myVariable.
NSObject has a method: (NSString *)description
This method will define which is printed out when you put object on NSLog method.
NSNumber inherits NSObject and description method is implemented in the way that primitive value will be printed out, and that's why you saw expected value using NSLog(#"myVariable %#", self.myVariable);
Operator "==" will compare 2 objects in this case (compare both pointer and value) ==> it will return false in your case
If you want to compare primitive value of 2 NSNumbers, use following method instead (BOOL)isEqualToNumber:(NSNumber *)number;

Polymorphic methods in Objective C

In Java you can put in multiple constructors to a class that are called depending on the types and/or number of parameters that are used when an instance is constructed.
I assume that there is the equivalent in Objective C.
Can I have a polymorphic method?
I would like to build a method that acts slightly differently according to whether a string is passed or a double?
Does that sound bonkers or is it easy?
You're thinking of overloaded methods. Due to the way dynamic dispatch is implemented in Objective-C, it isn't currently possible to pass two unrelated types as arguments to the same (or same-named) method and have it understand.
In Objective-C, there are two related but distinct approaches to handling multiple kinds of input. Let's use your example of a string or a double as possible inputs. In Java, you might have:
void applyWidget(String s);
void applyWidget(double d);
And that's great, but not Objective-C. In Objective-C, you instead would use two different method names:
- (void)applyWidgetWithName: (NSString *)name;
- (void)applyWidgetWithValue: (double)value;
The same logic is in each method as in the Java version, but the distinct names let the compiler treat them as distinct methods (which they are, even in Java.) The code also becomes self-documenting: by reading it, you get an idea of what's happening even without comments. Alternatively, if you simply must have one method name, you change the parameter type to id and accept any object:
- (void)applyWidget: (id)widget;
Then pass either an NSString or an NSNumber wrapping your double. Then, in the implementation of the method, use Objective-C's introspection methods to determine how to proceed:
if ([widget isKindOfClass: [NSString class]]) {
...
} else if ([widget isKindOfClass: [NSNumber class]]) {
double d = [widget doubleValue];
...
}
This approach essentially tells callers "send anything--I'll handle it appropriately." It can be difficult to determine the behaviour of such a method without extensive documentation.
Absolutely easy:
- (id)initWithSomeObject:(id)object
{
if ([object isKindOfClass:[ClassOne class]) {
// do something
} else if ([object isKindOfClass:[ClassTwo class]) {
// do something else
} // etc.
return self;
}
yes, but objc does not have proper overloading.
so you see things like initWithDouble:, initWithBool: and so on. that's part of the reason it's a bit 'wordy' for some people's taste.
to use your example:
#interface MONClass
- (id)initWithString:(NSString *)pString;
- (id)initWithDouble:(double)pDouble;
...
but the following is an error:
- (id)initWith:(NSString *)pString;
- (id)initWith:(double)pDouble;
because the selector is the same -- the parameter/return types are omitted from the selector.
Basically Objective C does't have proper method overloading. It will support overriding only.
Suppose if you write functions like in same class,
(void) showMethod;
(void) showMethod:(int) aNumber;
This will support in Objective C.
Suppose if you write functions like,
(void) showMethod:(NSString*) aString;
(void) showMethod:(int) aNumber;
In this way the compiler gives Error because there conflicting parameter types in implementation of showMethod.

How to implement equality based on property values?

I’m implementing -isEqual: for my custom class. The equality is based on the property values, ie. if all properties are equal, the objects are considered equal. Together with the traditional class check the code looks like this:
- (BOOL) isEqual: (id) object
{
return [object class] == [self class]
&& [[object someProperty] isEqual:someProperty]
&& [[object otherProperty] isEqual:otherProperty];
}
But this fails for nil property values, ie. two objects of the class having nil values stored in someProperty are considered non-equal, whereas I would like them to be equal. Thus I arrived at the following version:
- (BOOL) isEqual: (id) object
{
#define equals(a, b) ((a == b) || ([a isEqual:b]))
return equals([object class], [self class])
&& equals([object someProperty], someProperty)
&& equals([object otherProperty], otherProperty);
}
This seems to work fine. Is this the “standard” way to solve the equality? Seems overly complex to me.
isEqual: is very much object-specific. It's a semantic equality. It is up to you to define for every class what isEqual means. Thus there is no standard way to do it.
The simplest implementation is return self == object, your second implementation is very generic and nice, but not necessarily well suited to every class. Per example, for a Person, comparing the emails could be sufficient, or the emails and first names if you suppose an email could be used by several family members.

if statement fails with variable from CoreData managed object

I'm having some trouble with Managed Objects... imagine that.
Here is one real doozy, maybe someone can help.
So I have a managed object class called "Format" which is an entity. Anyway, it has a property called "slot" that's an NSNumber. Now the number can have values from zero to four, but if it does not have a value then I want the NSNumber object to be equal to "nil" as opposed to zero. I wasn't having any luck with that since evidently being zero is the same as being "nil." Ugh. (Is there a way to tell if the pointer is simply empty, "nil," as opposed to pointing to a zero value?)
At any rate, I tried a work-around which was to render the variable into text like so:
if(#"myFormat.slot" != #"(null)")
NSLog(#"slot==%#",myFormat.slot);
But the problem is that I got this in my log:
slot==(null)
So, OK... what the heck? If #"myFormat.slot" == #"(null)" then how the heck is that if statement resolving...?!?!
I'm truly baffled now... please someone help me out.
You won't ever get a nil back from an attribute. Instead, you get a [NSNull null] object.
In Objective-C, nil and null are not interchangeable. When you see nil you are almost looking at a dereferenced pointer. nil is intended to convey that no object has been assigned to the symbol. null by contrast is the singleton instance of [NSNull null]. It is used as a placeholder to indicate that some value, represented by an object, has not been set. In other words, a value of nil doesn't make sense in Objective-C.
In Core Data, relationships and attributes are not treated the same even though they both return objects. A relationship is a pointer to an external object and therefore can have a return nil if no object has been set on the other side of the relationship. An attribute is a value only held by an object and therefore is always represented by [NSNull null] if not set.
By default, all attributes with numerical value will return an NSNumber object initialized to zero. If you remove the default you get [NSNull null].
However, since [NSNull null] is a singleton you can use a simple pointer comparison to check for it e.g.
if (myMo.numericalAttribute == [NSNull null])...
However, that is considered bad practice.
This if(#"myFormat.slot" != #"(null)") is always true, because #"myString" creates an autoreleased string. Therefore you are checking the addresses of to different autorelease strings and (surprise) they are different.
Have you tried something like this:
if ([myNumber isEqualTo: [NSNumber numberWithInt: 0]]) self.myNumber = nil;
When comparing the contents of 2 NSString objects, use the isEqual: method or, if you need to perform a Unicode-based comparison of strings, use isEqualToString:.
If you want to set myFormat.slot to nil, do this:
myFormat.slot = nil;
To set the value of the slot attribute to zero:
myFormat.slot = [NSNumber numberWithInt:0];
To compare values wrapped in NSNumbers (e.g. check if your slot is equal to the 0 integer value), do this:
if ([myFormat.slot intValue] == 0) { // primitive type comparison
//...
}
or this:
if ([myFormat.slot isEqual:[NSNumber numberWithInt:0]]) { // object comparison
//...
}
but NOT THIS:
if (myFormat.slot == 0) { // pointer comparison!!
//...
}
To check if slot is empty (nil):
if (myFormat.slot == nil) {
//...
}
Keep in mind: Core Data standard attributes are always mapped to non-primitive values (NSString, NSDate, NSNumber). Always use isEqual:, compare: & friends when comparing objects' values. The == operator performs pointer comparison when you use it with non-primitives.

Comparing objects in Obj-C

How does one compare objects in Objective-C?
Is it as simple as == ?
I want to check an array for an object and if it doesnt exist add it to the array otherwise, remove it from the array.
Comparing objects in Objective-C works much the same as in Java or other object-oriented languages:
== compares the object reference; in Objective-C, whether they occupy the same memory address.
isEqual:, a method defined on NSObject, checks whether two objects are "the same." You can override this method to provide your own equality checking for your objects.
So generally to do what you want, you would do:
if(![myArray containsObject:anObject]) {
[myArray addObject:anObject];
}
This works because the Objective-C array type, NSArray, has a method called containsObject: which sends the isEqual: message to every object it contains with your object as the argument. It does not use == unless the implementation of isEqual: relies on ==.
If you're working entirely with objects that you implement, remember you can override isEqual: to provide your own equality checking. Usually this is done by comparing fields of your objects.
Every Objective-C object has a method called isEqual:.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/isEqual:
So you would want to override this for your custom object types.
One particular important note in the documentation:
If two objects are equal, they must
have the same hash value. This last
point is particularly important if you
define isEqual: in a subclass and
intend to put instances of that
subclass into a collection. Make sure
you also define hash in your subclass.
== will compare the pointer, you need to override
- (BOOL)isEqual:(id)anObject
Implement isEqual: and hash
Per the Apple documentation on NSObject you need to implement isEqual: and hash at a minimum. Below you'll find one way to implement object equality, of course how to implement hash enters the land of serious debate here on StackOverflow, but this will work. General rule - you need to define what constitutes object equality and for each unique object they should have a unique hash. It is best practice to add an object specific equality method as well, for example NSString has isEqualToString:.
- (BOOL)isEqual:(id)object
{
BOOL result = NO;
if ([object isKindOfClass:[self class]]) {
CLPObject *otherObject = object;
result = [self.name isEqualToString:[otherObject name]] &&
[self.shortName isEqualToString:[otherObject shortName]] &&
[self.identifier isEqualToString:[otherObject identifier]] &&
self.boardingAllowed == [otherObject isBoardingAllowed];
}
return result;
}
- (NSUInteger)hash
{
NSUInteger result = 1;
NSUInteger prime = 31;
result = prime * result + [_name hash];
result = prime * result + [_shortName hash];
result = prime * result + [_identifier hash];
result = prime * result + _boardingAllowed;
return result;
}