I am trying to do array comparison on 2 mutable arrays. In one of the array, i am storing my model objects and in the other array I am storing a copy of the model objects using [myObject copy]. My model object is a subclass of NSObject so I have added the copyWithZone: method as well. However when I do array compare using isEqualToArray on these two arrays it always returns false. Will the compare not work on copied objects? Or am I going wrong somewhere else?
P.S: As an overview, what I'm trying to do is to check whether something is changed in my model before calling an update service. I want to call the service Only if any of the model objects have changed.
Will the compare not work on copied objects?
You can very easily find out the answer to this question by just copying a single object and checking for equality agains the original.
SPOILER
The results you are going to see will depend on if you have implemented custom hash and isEqual: methods in your class. Without those it will default to the superclasses implementation (NSObject) which considers equality to be the same pointer. Since a copy is a new pointer to the same object, NSObject won't consider them equal.
I would recommend that you read about object equality in this NSHipster article (great to start with) and/or in this article by Mike Ash (if you are feeling curious)
Method isEqualToArray acts as follows. It takes one by one the next objects from two arrays and compare them using isEqual. The latter compares hash (NSInteger property) of NSObjects (or its subclasses).
In general it is the address of the object. hash can be redefined while subclassing but it may cause big problems.
For copied objects you will have different hashes. And thus isEqualToArray is always FALSE.
But if you use simple data classes like NSNumber, NSString as elements to compare, you will get TRUE under copying them.
Related
How does NSCoding deal with encoding and decoding of pointers to other objects rather than values? I have a range of model classes which I need to encode, and they need to reference each other with weak properties. I was surprised to find that the references seemed to be maintained after encoding, saving to a file, then subsequently reading from the file and decoding. This worked even though the memory addresses of the objects which referenced each other changed. I'm interested to know how NSCoding achieves this, and also want to ensure it will work consistently.
In short: if I encode properties containing pointers to other objects, can I rely 100% on the references being maintained after decoding?
tl;dr: Yes, NSKeyedArchiver and NSKeyedUnarchiver maintain the reference relationships of the objects in your object graph; if you encode an object graph where objects reference other objects, you can expect the graph to be decoded in a way that maintains those relationships. You won't necessarily get back the same pointers after encoding and decoding, but the identities of the objects will remain the same.
When you encode an object via -encodeObject:forKey:, NSKeyedArchiver never writes out the pointer address of the object. Instead, it maintains a unique table of objects it's seen before:
If the object you're encoding has never been seen before, it gets added to the table and assigned a UID. It then has -encodeWithCoder: called on it so it can encode all of its properties
If it's been seen before (and has been assigned a UID), nothing happens (but the UID is recorded in its place)
When archiving is complete, the archive contains the unique table of objects, along with another dictionary that represents the structure of the object graph. In place of objects are UIDs.
On decode, when you go to call -decodeObject:forKey:, NSKeyedUnarchiver does the opposite mapping:
It looks at the UID for the key in the archive and searches for the UID in the unique table. If that object has been decoded before, it returns a reference to the decoded object. This is the key part — you won't necessarily get the same pointer on decode that was encoded on encode, but you'll get a reference to the same object instead of a copy
If the UID's never been decoded before, it looks for the encoded object in the unique table corresponding to the UID, looks up the class and calls +alloc, -initWithCoder:, and -awakeAfterUsingCoder on the object. The object is then recorded in the UID-to-object map to be reused if necessary
This is one of the key features of the keyed archiver system, and you can rely on this behavior being maintained.
I want to know how NSSet make sure that it has only unique objects? When we try to add duplicate value, then on which criteria it decides that it has already this value? I want to know the underlying uniqueness criteria.
NSSet uses your object's implementation of isEqual: method to decide object equality. It also uses hash to make the lookup much faster.
-(BOOL)isEqual:(id)other;
-(NSUInteger)hash;
When two objects are equal, their hash methods must return the same value (objects with the same hash, however, may not necessarily be equal to each other).
When your hash and isEqual: are implemented properly, NSSet can decide the equality by checking only a handful of objects whose hash "collides" with the hash of the object you are adding to the set.
take a look at Object Comparison in the official apple documentation. As you can see there most of the containers use hash to compare objects.
I want to know how NSSet make sure that it has only unique objects
Whenever you try to add object it will check for hash value of all other existing objects inside of it. Based on that it will keep it as unique
If two objects are equal, they must have the same hash value
When we try to add duplicate value, then on which criteria it decides that it has already this value?
if the new object hash value matches the existing then it will be considered as dublicate
Refer this Apple's documentation
First of all the set checks hash values of objects. If hashes are not equal it means that objects are guaranteed to be different. If hashes are equal it however doesn't mean that objects are neccessarily equal, so the set has to make sure and check their isEqual: methods
My problem is...
I have data that gets pulled from a XML file and parsed then stored in an array.
This data is updated every ~60 minutes.
Now i created a second array that parses the same data but only once a week.
What happens now is that i have an array that is updated every 60 minutes and another that is updated every 7 days.
The reason for this, is that i'm doing a table of teams and i want to check when a team has moved up or down in the standings.
So my first issue is tackled, i have 2 arrays containing the teams one with up to date data and the other with a week old data.
My second issue is how do i compare these objects indexes? each object has many properties, as .name or .teamid
Can someone provide me a sample code on how to compare ArrayA Object.name to ArrayB.name?
Thank you so much.
Your question is confusing; apparently you have written an application which is capable of pulling data from XML files, parsing it, creating an array, and doing all this to a time schedule and yet don't know how to compare two items? It sounds like I'm misunderstanding your question! So here is a guess, maybe it will help:
Cocoa has a pattern for equality of objects - two methods isEqual: and hash work together to provide a standardised way of testing whether two object instances are "equal", whatever that means in terms of what the objects represent.
These methods are provided by all the standard classes, so you can test whether two NSString instances represent the same string etc. For a collection class such as NSArray its isEqual: method compares the elements in the two arrays pairwise by calling their isEqual: methods etc.
The base class, NSObject, provides a default implementation, but as it cannot know what equality means for a particular class its implementation is very basic - two objects are equal if and only if they are exactly the same object, i.e. [a isEqual:a] is YES and nothing else is.
So you need to provide an isEqual: and (this is important) hash pair for your class, which implements what is equality for your class - which is probably defined in terms of some of its properties, such as the name you mention, and will invoke their isEqual: methods.
Of course if equality of your class is simply based on one of its properties, such as name, implementing isEqual: & hash is trivial - the implementations just call the same methods on that property. In this case you don't even need to implement the pair yourself, you can access the property can compare it directly. Something like:
NSUInteger indexOfInterest = ...;
MyClass *objectA = arrayA[indexOfInterest];
MyClass *objectB = arrayB[indexOfInterest];
BOOL itemsAreEqual = [objectA.name isEqual:objectB.name];
If your property is not an object type but some primitive type, such as int, then you just use the standard equality operator:
BOOL itemsAreEqual = objectA.name == objectB.name;
Read: Object Comparison from Apple's Cocoa Core Competencies.
I have 2 Core Data objects of the same type. I got them via different ways.
At the same time these objects have an NSString field (identifier).
So what is better?
1)compare objects via isEqual:
2)compare objects via == (is it possible in general?)
3)compare objects' fields via isEqualToString:?
Basically, never use == on objects. Always use isEqual:.
If you're using a predicate then you can use LIKE on the identifier.
If you are trying to determine whether or not two objects are the semantically equal and you can describe what equality looks like for that type, then use isEqual. For example, if you have a Person class, you might define equality based on SSN (in the US) or something equally unique.
If you are trying to determine that two objects have something specific in common, but it doesn't necessarily imply that they are equal (for example, using the same Person example, if you want find out if two instances have the same last name), compare the attributes using isEqualToString:. It would not make sense to put this comparison into isEqual for the type.
If you are trying to find out if you have two NSManagedObject instances from your persistent store that are actually the same object fetched via different NSManagedObjectContexts, use the objectID for comparison.
Assuming I have a hash value of some NSObject during runtime.
Is there a way to find a pointer to that object using just hash value?
I don't want to store pointers to objects and their hashes as keys. I imagine that iOS already doas that.
There is no way, not even an unreliable way, to do this.
Many objects have hashes in ways that makes it impossible to reference it. You will have duplicates because of this. One example, as #Martin said, is NSArrays. NSArrays' hashes are just the number of elements in the array.