BREAKING UPDATE: Actually comparing the -absoluteURL or -absoluteString of two NSURLs that represent a file is not good enough. One may start file:///, and the other file://localhost/, and they will not be isEqual:! A work around is to compare the path of each NSURL. I’m still looking into the issue, but for now I am using the following method to compare NSURLs.
@implementation NSURL (IsEqualTesting)
- (BOOL) isEqualToURL:(NSURL*)otherURL;
{
return [[self absoluteURL] isEqual:[otherURL absoluteURL]] ||
[self isFileURL] && [otherURL isFileURL] &&
([[self path] isEqual:[otherURL path]]);
}
@end
[a isEqual:b] may report NO for two NSURLs that both resolve to the same resource (website, file, whatever). So compare NSURLs like [[a absoluteString] isEqual:[b absoluteString]]. It’s important to be aware of this gotcha, because URLs are Apple’s preferred way to represent file paths, and APIs are starting to require them. Equality tests that worked for NSString file-paths may fail with NSURL file-paths.
The official documentation says
two
NSURLsare considered equal if they both have the same basebaseURLandrelativeString.
Furthermore,
An
NSURLobject is composed of two parts—a potentially nil base URL and a string that is resolved relative to the base URL. AnNSURLobject whose string is fully resolved without a base is considered absolute; all others are considered relative.
In other words, two NSURL objects can resolve to the same absolute URL, but have a different base URL, and be considered !isEqual:.
An example should make this all clear,
NSURL *VGableDotCom = [NSURL URLWithString:@"http://vgable.com"]; NSURL *a = [[NSURL alloc] initWithString:@"blog" relativeToURL:VGableDotCom]; NSURL *b = [[NSURL alloc] initWithString:@"http://vgable.com/blog" relativeToURL:nil]; LOG_INT([a isEqual:b]); LOG_INT([[a absoluteURL] isEqual:[b absoluteURL]]); LOG_ID([a absoluteURL]); LOG_ID([b absoluteURL]);
[a isEqual:b] = 0
[[a absoluteURL] isEqual:[b absoluteURL]] = 1
[a absoluteURL] = http://vgable.com/blog
[b absoluteURL] = http://vgable.com/blog
Remember that collections use isEqual: to determine equality, so you may have to convert an NSURL to an absoluteURL to get the behavior you expect, especially with NSSet and NSDictionary.
Great solution, made me feel more sane :-)
Comment by Tony Mann — February 11, 2014 @ 4:22 pm