I have some code that loops through an array of NSManagedObjects and stops when it finds a certain record that is stored in an instance variable. The only way I can manage to see if they are the same record (not an equivalent record, the specific record) is by comparing the URIRepresentations of the objectIDs. This surely cannot be the best way of doing it. I'm doing:
if ([[[obj1 objectID] URIRepresentation] isEqualTo: [[_obj2 objectID] URIRepresentation]]) {
NSLog(#"Match");
}
The following code never matches even when I NSLog the objectIDs and see that they are in fact exactly the same.
if ([[obj1 objectID] isEqualTo: [_obj2 objectID]]) {
NSLog(#"Match");
}
The commenter is correct, isEqualTo: will not work in this case since they are different instances of NSManagedObjectID.
The way you are doing this is actually the best way, the objectID is CoreData's unique identifier for a given managed object, it's the only way to tell if two instances of NSManagedObject point to the same record in the persistent store.
Clarification:
ImHuntingWabbits refers to isEqual:, but then advises Nick to stick with his first example, which actually uses isEqualTo:.
Per Peter Hosey's comment to the post isEqual vs isEqualTo, there is a difference and you're better off using isEqual:.
Following the present posts, I originally used isEqualTo: to compare objectID URLs, which worked fine in Cocoa, but when I moved this code over to iOS, I got warnings that "NSURL may not respond to isEqualTo." When I changed to isEqual:, the warnings went away.
So if you're following these examples, you should probably do this:
if ([[[obj1 objectID] URIRepresentation] isEqual:[[_obj2 objectID] URIRepresentation]] {
NSLog(#"Match");
}
-isEqual: has been implemented correctly for NSManagedObjectID in CoreData. We use collections with -contains: all the time. It seems that the original poster confused -isEqual: with -isEqualTo: The latter should not be used.
Related
I have a dictionary.
I extract one of its values as follows:
NSString *magicValue= [filterDict valueForKey:[filterDict allKeys][0]];
[SomeClass foo: magicValue];
And foo is:
- (void)foo:(NSString*)magicValue
{
NSLog("magicValue is string:%#",[magic isKindOfClass:[NSString class]] ? #"YES" : #"NO");
NSLog("magicValue is number:%#",[magic isKindOfClass:[NSNumber class]] ? #"YES" : #"NO");
}
If the dictionary value is number magicValue will be NSNumber. So the defined string pointer will be pointing to an NSNumber. The log will return yes for the number check.
I never added protection to such methods, to check what class "magicValue" is. I assumed that when I define a method with string parameter it will be string.
Should I start accounting for such behavior and always add checks, or is it the fault of the guy that assigned that dictionary value to magic in such a way and used my method. I need some best practices advice and how to handle this.
This question could have already been answered but I didn't know how to search for it.
Short answer: No, do not check that, if there is no special reason.
Long answer:
You have to differentiate between two cases:
A. Using id
You have a variable or a return vale of the type id. This is in your example -valueForKey:. The reason for that typing is to keep the method generic. Even it is theoretically possible, in practice a type mismatch in such situation is very rare and detected fast in development. With a different motivation I asked the audience (>200) in a public talk, how many times they had such a typing error in production. For all listeners, all of their apps in all of the app's versions there was 1(in words: one!) case. Simply forget about that risk. It is the anxiety of developers using statically typing languages (Java, C++, Swift).
B. Wrong assignment
If you do not have an id type, it is still possible to do such tricks. (And sometimes you want to do that. That is a strength of dynamic typing.) There are two subcategories:
You can do it implicitly:
NSString *string = [NSNumber numberWithInt:1];
The compiler will warn you about that. So everything is fine, because the developer will see his mistake. You do not have to protect him or your code.
One can do it explicitly:
NSString *string = (NSString*)[NSNumber numberWithInt:1];
In such a case, code can break. But he did it explicitly. If it is wrong, the developer had criminal energy to do so and you do not have to protect him from himself.
Most of the time you should know what class you're referencing, or at least what you intend it to be. On the occasions where you have an uexpected class which can cause a crash depending on what messages you send to it, you can then debug your code and get the correct reference.
There are times, usually when dealing with inheritance, when you need to determine the class at runtime rather than at compile time. This is when, isKindOfClass: can be useful. If you know that a value could be one of many classes, I would extract it as an id and then cast it at the last moment e.g.
id value = [[NSUserDefaults standardUserDefaults] valueForKey:aKey];
if ([value isKindOfClass:[MyClass class]]) {
// Do one thing
}
else {
// Do another
}
Follow is some code for example.
NSArray *test1 = [[NSArray alloc] initWithObjects:#"TEST", nil];
[someArray addObject:test1];
:
:
too many code lines.
:
:
At some place
NSArray *addingArray = [test1 whoisAddingOrContainingMe(?)];
I want to know a pointer of someArray as method of test1 instance.
Is there a method like this?
No, you can't "reverse lookup" the containers you are contained in.
From a design perspective this would be somewhat difficult, since conceptually there's no difference between having a reference to oneself in an "array", in any other container, or in any other object that's not considered to be a container. Thus, you have to record every single "retain" by passing it an additional "owner" parameter, and since retains and releases can be done in vastly different places you would also need to pass "owner" pointers around so that an eventual "release" can refer to the proper retain.
Or, to put it short: it would be a huge mess :-)
As suggested before, if you know what arrays can actually contain you -- and that should be much easier for your application -- you could check them. Or you could add a list to the objects to record where they have been added, probably via methods like "addTo:" and "removeFrom:".
I think you want NSArray's -containsObject: method.
I hardly ever see the second one used and I wonder why?
Neither would it break support for situations where an NSArray is expected (as it's a subclass).
Nor would it break encapsulation by revealing mutable internals.
Under the precondition that it's never a mutable ivar that's returned, (which should be common sense anyway)
I can right now only think of advantages of using the second.
It actually is mutable. And muting is safe here, so why prevent it?
No need to call [[[foo fooBar] mutableCopy] autorelease], which needlessly allocates additional memory and needlessly wastes time.
Here are the method variations:
- (NSArray *)fooBar {
NSMutableArray *fooArray = [NSMutableArray array];
//populate fooArray
return fooArray;
}
- (NSMutableArray *)fooBar {
NSMutableArray *fooArray = [NSMutableArray array];
//populate fooArray
return fooArray;
}
I'm asking as my project has a bunch of methods with the same pattern.
And in most of the times the returned array will be modified afterwards (merged, edited, etc).
So I think it should be totally fine to return NSMutableArrays, yet nobody seems to be doing it.
NSMutableArray, NSMutableSet, NSMutableDictionary… it's basically the same deal.
For an explanation of using mutable versus immutable, check out Apple's documentation on Object Mutability.
In general, it is best to return an immutable version, unless it is specifically your intent that the object returned always be an immutable object available for any client to change. You should create your interfaces based on the intent of the interface, not off the current implementation. It is possible that requirements will change and you will need to change the implementation of fooBar such that it does return an instance variable. By returning mutable arrays you ensure that you encapsulate not only your instance variables, but your current implementation.
So, you may have a valid place to return a mutable array (I don't know), but you see most code passing immutable arrays because it fully encapsulates their variables and their implementations.
I suppose the first variation was preferred because polymorphism was preferred.
In either case, both methods return an instance of NSMutableArray, the only difference being that the first one hides that fact from the caller. In other words, the first variation is not safer than the second. It's essentially using polymorphism to tell the caller that any type of NSArray might be returned. If you need that kind of flexibility in your code, it definitely has it's advantages. (e.g., if one day, for whatever reason, you need to return a custom NSArray subclass, your code won't break at that level).
However, you seem to prefer communicating intent to the caller - i.e. that you actually return mutable arrays - which is also OK. To make everyone happy (if there is such thing anyways...), I suggest renaming the 2nd method to:
- (NSMutableArray *)mutableFooBar {
NSMutableArray *fooArray = [NSMutableArray array];
//populate fooArray
return fooArray;
}
As a side note, I think that the following is a slightly more efficient way to convert an existing immutable array into a mutable one:
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:fooArray];
(correct me if I'm wrong on that assumption).
I hope this answers your question...
Having a method return a mutable instance like that looks suspicious.
As the caller you have to question the original method signature and wonder if it really is safe to mutate the returned value. After all the class may inadvertently be returning a pointer to internal state.
If profiling reveals that this copy is indeed expensive, I usually change the method signature to make it obvious that the mutability is intended. Perhaps with something like:
- (void)populateFooBars:(NSMutableArray *)array;
That way it is clear that the mutability of the result is intentional.
I'm trying to determine if two NSSets are "equal" but not in the sense of isEqualToSet. Items in the two sets are the same class but are not the same object, or even references to the same object. They will have one property that is the same though - let's call it 'name'.
Is my best bet in comparing these two sets to do a simple set count test, then a more complex objectsPassingTest: on each item in one set, making sure an item with the same name is in the other set? I'm hoping that something simpler exists to handle this case.
I had the same problem, but I needed to compare multiple properties at the same time (class User with properties Name and Id).
I resolved this by adding a method returning an NSDictionary with the properties needed to the class:
- (NSDictionary *)itemProperties
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setObject:self.name forKey:#"name"];
[dict setObject:self.id forKey:#"id"];
return dict;
}
and then using valueForKey: as Kevin Ballard mentioned:
BOOL userSetsEqual = [[userSet1 valueForKey:#"itemProperties"]
isEqualToSet:[userSet2 valueForKey:#"itemProperties"]];
... where userSet1 and userSet2 were the NSSets that contained User objects.
You could just call valueForKey: on both sets and compare the results.
if ([[set1 valueForKey:#"name"] isEqualToSet:[set2 valueForKey:#"name"]]) {
// the sets match your criteria
}
Looking through the documentation, it seems that there is no way to really handle this special case of yours. You're going to have to write some custom code to handle this. Personally, I would recommend using -sortedArrayUsingDescriptors: and then comparing the arrays, but that's just me. You could also go enumerate through one set, then narrow down the other using -filteredSetUsingPredicate: and get its count.
Whichever method you use, consider the fact that its probably not going to be super efficient. This might be unavoidable, but there are probably ways to go about it that are better than others. Food for thought.
The documentation for -hash says it must not change while a mutable object is stored in a collection, and similarly the documentation for -isEqual: says the -hash value must be the same for equal objects.
Given this, does anybody have any suggestions for the best way to implement -hash such that it meets both these conditions and yet is actually calculated intelligently (i.e. doesn't just return 0)? Does anybody know how the mutable versions of framework-provided classes do this?
The simplest thing to do is of course just forget the first condition (about it not changing) and just make sure I never accidentally mutate an object while it's in a collection, but I'm wondering if there's any solution that's more flexible.
EDIT: I'm wondering here whether it's possible to maintain the 2 contracts (where equal objects have equal hashes, and hashes don't change while the object is in a collection) when I'm mutating the internal state of the object. My inclination is to say "no", unless I do something stupid like always return 0 for the hash, but that's why I'm asking this question.
Interesting question, but I think what you want is logically impossible. Say you start with 2 objects, A and B. They're both different, and they start with different hash codes. You add both to some hash table. Now, you want to mutate A, but you can't change the hash code because it's already in the table. However, it's possible to change A in such a way that it .equals() B.
In this case, you have 2 choices, neither of which works:
Change the hashcode of A to equal B.hashcode, which violates the constraint of not changing hash codes while in a hash table.
Don't change the hashcode, in which case A.equals(B) but they don't have the same hashcodes.
It seems to me that there's no possible way to do this without using a constant as a hashcode.
My reading of the documentation is that a mutable object's value for hash can (and probably should) change when it is mutated, but should not change when the object hasn't been mutated. The portion of the documentation to which to refer, therefore, is saying, "Don't mutate objects that are stored in a collection, because that will cause their hash value to change."
To quote directly from the NSObject documentation for hash:
If a mutable object is added to a
collection that uses hash values to
determine the object’s position in the
collection, the value returned by the
hash method of the object must not
change while the object is in the
collection. Therefore, either the hash
method must not rely on any of the
object’s internal state information or
you must make sure the object’s
internal state information does not
change while the object is in the
collection.
(Emphasis mine.)
The question here isn't how to meet both of these requirements, but rather which one you should meet. In Apple's documentation, it is clearly stated that:
a mutable dictionary can be put in a hash table but you must not change it while it is in there.
This being said, it seems more important that you meet the equality requirement of hashes. The hash of an object should always be a way to check if an object is equal to another. If this is ever not the case, it is not a true hash function.
Just to finish up my answer, I'll give an example of a good hash implementation. Let's say you are writing the implementation of -hash on a collection that you have created. This collection stores an array of NSObjects as pointers. Since all NSObjects implement the hash function, you can use their hashes in calculating the collection's hash:
- (NSUInteger)hash {
NSUInteger theHash = 0;
for (NSObject * aPtr in self) { // fast enumeration
theHash ^= [aPtr hash];
}
return theHash;
}
This way, two collection objects containing the same pointers (in the same order) will have the same hash.
Since you are already overriding -isEqual: to do a value-based comparison, are you sure you really need to bother with -hash?
I can't guess what exactly you need this for of course, but if you want to do value-based comparison without deviating from the expected implementation of -isEqual: to only return YES when hashes are identical, a better approach might be to mimick NSString's -isEqualToString:, so to create your own -isEqualToFoo: method instead of using or overriding -isEqual:.
The answer to this question and the key to avoiding many cocoa-bugs is this:
Read the documentation carefully. Place every word and punctuation on a golden scale and weight it as it was the world's last grain of wheat.
Let's read the documentation again:
If a mutable object is added to a collection that uses hash values to determine the object’s position in the collection, [...]
(emphasis mine).
What the writer of the docs, in his/hers eternal wisdom, mean by this is that when you are implementing a collection, like a dictionary, you shouldn't use the hash for positioning since that can change. In other words it has little to do with implementing -hash on mutable Cocoa objects (which all of us thought it had, assuming the documentation has not changed in the last ~10 years since the question was asked).
That is why dictionaries always copy their keys - so they can guarantee
that the hash value won't change.
You will then ask the question: But, good sir, how does NSMapTable and similar handle this?
The answer to this is according to the documentation:
"Its keys or values may be copied on input or may use pointer identity for equality and hashing."
(emphasis mine again).
Since we were so easily fooled by the documentation last time, let's run a little experiment to see for ourselves how stuff actually work:
NSMutableString *string = [NSMutableString stringWithString:#"so lets mutate this"];
NSString *originalString = string.copy;
NSMapTable *mutableStrings = [NSMapTable strongToStrongObjectsMapTable];
[mutableStrings setObject:originalString forKey:string];
[string appendString:#" into a larger string"];
if ([mutableStrings objectForKey:string] == nil)
NSLog(#"not found!");
if ([mutableStrings objectForKey:originalString] == nil)
NSLog(#"Not even the original string is found?");
for (NSString *inCollection in mutableStrings)
{
NSLog(#"key '%#' : is '%#' (null)", inCollection, [mutableStrings objectForKey:inCollection]);
}
for (NSString *value in NSAllMapTableValues(mutableStrings))
{
NSLog(#"value exists: %#", value);
}
Surprise!
So, instead of using pointer equality, they focus on the words "may" here which in this case mean "may not", and simply copy the hash value when adding stuff to the collection.
(All this is actually good, since it would be quite difficult to implement NSHashMap, or -hash, otherwise).
In Java, most mutable classes simply don’t override Object.hashCode() so that the default implementation returns a value that is based on the address of the object and doesn’t change. It might just be the same with Objective C.