15

I've noticed that the AFNetworking and SDWebImage categories on UIImageView have the same method name.

AFNetworking:

- (void)setImageWithURL:(NSURL *)url {
    [self setImageWithURL:url placeholderImage:nil];
}

- (void)setImageWithURL:(NSURL *)url 
       placeholderImage:(UIImage *)placeholderImage
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPShouldHandleCookies:NO];
    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];

    [self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}

- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest 
              placeholderImage:(UIImage *)placeholderImage 
                       success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
                       failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
{
...
}

and SDWebImage

- (void)setImageWithURL:(NSURL *)url
{
    [self setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder
{
    [self setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options
{
    [self setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}

...

In Xcode when I command-click the SDWebImage method it redirects me to the AFNetworking method and vice-versa.

Which behavior can I expect where? Should I only include the header for the the appropriate category in a class I'd like to use it in? What if a same class needs to use both implementations of the category?

Another related question "What happens if two ObjC categories override the same method?" isn't quite the same as this because both AFNetworking and SDWebImage are adding Categories onto the same class not a subclass. In this case only once class is being used and 2 categories seem to be in conflict.

Community
  • 1
  • 1
PaulWoodIII
  • 2,636
  • 7
  • 31
  • 35
  • 1
    Related: ["What if multiple categories implement the same method?"](http://stackoverflow.com/a/1085500/335858). – Sergey Kalinichenko Feb 06 '13 at 04:01
  • This is Bad, and it's exactly why third-party frameworks are strongly recommended to use prefixes on methods which they add to other frameworks' classes via categories. Header files have no effect on this; the runtime loads the categories regardless, and the order, and therefore the implementation you end up with, is undefined. – jscs Feb 06 '13 at 04:08
  • ***Not*** a dupe of [What happens if two ObjC categories override the same method?](http://stackoverflow.com/q/3221972) Probably a dupe of [Extending a class using categories with identical methods name](http://stackoverflow.com/q/4764977), [Overriding methods using categories in ObjC](http://stackoverflow.com/q/5272451), and [Override a method in ObjC via category](http://stackoverflow.com/q/14259517) – jscs Feb 06 '13 at 06:33

3 Answers3

8

Name collision is indeed something that can happen in Objective-C runtime... Apple advises the use of 'prefix' on methods names.

The expected behaviour : only the last category/method loaded by the runtime will be effective. Which one is it ? Bad question !

My advice : rename !

Vinzzz
  • 11,746
  • 5
  • 36
  • 42
  • 2
    The headers have no effect on this behavior; if the implementation files are being compiled, the collision will occur, and the order of loading at runtime is undefined (though likely repeatable). – jscs Feb 06 '13 at 04:07
  • So an answer for some projects will be to fork these projects on Github and rename the methods names to not conflict, maybe package them up in a cocoapod and move on with their code? Thankfully that isn't to hard! – PaulWoodIII Feb 06 '13 at 04:14
  • you can still send a pull request to the project owners, if you just add prefixes to avoid collision, this will make their code better ;) – Vinzzz Feb 06 '13 at 04:16
  • The problem with that, of course, is that everyone will have to update their method calls when they start using the new version of the framework... @ChinaPaul – jscs Feb 06 '13 at 04:32
  • 1
    The behavior is undefined, in fact. It will not necessarily be the last one loaded. There is a particular situation where the first mach-o image's method will win (which is subtle, but was the reason why the error message was changed when the collision happens). – bbum Feb 06 '13 at 05:24
  • Ok, I can remove that part too (I hesitated to remove my answer, but found the comments interesting). In Which situation is the first method winning ?(source) – Vinzzz Feb 06 '13 at 05:38
  • @JoshCaswell I don't think this is a duplicate of http://stackoverflow.com/questions/3221972/what-happens-if-two-objc-categories-override-the-same-method because that question uses subclassing. This is two categories on one class, no? – PaulWoodIII Feb 06 '13 at 06:20
  • @ChinaPaul: Shit, yes, you're right, I didn't read that thoroughly enough. Apologies. However, there are still a few others that (after really reading!) I think _are_ the same: [Extending a class using categories with identical methods name](http://stackoverflow.com/q/4764977) and [Overriding methods using categories in ObjC](http://stackoverflow.com/q/5272451), [Override a method in ObjC via category](http://stackoverflow.com/q/14259517) – jscs Feb 06 '13 at 06:26
  • For future reference to the others out there. I added the prefix Vinzzz mentioned on SDWebImage https://github.com/PaulWoodIII/SDWebImage – PaulWoodIII Feb 06 '13 at 06:44
8

Rename doesn't seem to be the best way to resolve the problem,

just use :

[cell.imageView setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:@"placeholder"] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {

}];

from SDWebImage which doesn't exist in AFNetworking

Wooble
  • 87,717
  • 12
  • 108
  • 131
bourou01
  • 81
  • 2
  • Yes that is true and would work for the example but on the higher level, away from AFNetworking/SDWEbImageCache example, "What if a same class needs to use both implementations of the category?" I think if this is the case you should rename the category. – PaulWoodIII Mar 18 '13 at 02:23
2

For anyone looking to use both SDWebImage and AFNetworking as-is, here's my caveman solution to discourage use of the colliding methods:

https://gist.github.com/sibljon/5957892

It's obviously not future proof, and is prone to breaking if the implementation of these methods changes in AFNetworking or SDWebImage. It's far from an ideal solution. In other words, at your own risk!

codeperson
  • 8,050
  • 5
  • 32
  • 51
  • As I understand the gist, the idea is to deprecate the method in question. I don't get any warning, however. Doesn't that also depend on, which category gets used? – Grav Feb 20 '14 at 08:47
  • 1
    I have added a way of stopping a build, if the colliding method is used. I've commented the gist. – Grav May 27 '14 at 11:04