13

If a method is defined in both a class and a category on that class, it is undefined which implementation will be called.

But how does this interact with inheritance? Specifically:

  • Given a superclass category method and a regular method in the subclass, is it guaranteed that the subclass implementation will win when called on a member of the subclass?
  • Given a superclass regular method and a subclass category method trying to override it, is it guaranteed that the subclass category implementation will win when called on a member of the subclass?
  • Given a superclass category method and a subclass category method, is it guaranteed that the subclass category method will win when called on a member of the subclass?
William Jockusch
  • 26,513
  • 49
  • 182
  • 323
  • I wouldn't say 'no way'. You can snag the original implementation in the category +load method and have some fun ;) – Joshua Weinberg Aug 10 '11 at 15:23
  • 3
    If a category overrides a method that exists in the category's class, the category method will be called. The conflict arises when two categories on the same class implement/override the same method. This is important because many Cocoa classes' methods are implemented in categories. If you try to override a framework-defined method, it may have been implemented in a category, and which implementation takes precedence is *undefined*. – albertamg Aug 10 '11 at 15:36

3 Answers3

49

Lets just put it this way. Don't override methods using categories, period, ever, end of answer.

Joshua Weinberg
  • 28,598
  • 2
  • 97
  • 90
40

If a method is defined in both a class and a category on that class, it is undefined which implementation will be called.

That is incorrect; the category method will always win. What won't work, though, is if you have multiple categories that implement the same method, then the "which one wins" is undefined.

It is generally "last loaded wins", but that really isn't a hard rule, either.

Note that since many classes will internally have their implementation dividing across categories for code organization purposes, you can't rely on the first rule anyway.

In short, what Joshua said; Do not override methods using categories.

Beyond the inheritance reasons, you are also viciously breaking encapsulation when you do so. It isn't that a category based implementation overrides an existing method, it entirely replaces it. Thus, if you don't reproduce every last internal implementation detail, including bugs, your replacement won't quite work right and debugging it will be hard.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • 1
    Well, that is part of my question. The superclass (UIView) is an Apple class. I have a category that adds a method; let's call it myUIViewCategoryMethod. If I override that method in a subclass, can I be sure that the subclass version will win? What if I instead use a category on a subclass? – William Jockusch Aug 10 '11 at 16:21
  • 2
    Yes -- subclass implementations will always win. – bbum Aug 10 '11 at 17:15
  • Subclassing is not always available, but at times, need arises for replacing or enhancing a certain functionality. In such cases, category override is never recommended. Instead, use the Objective C Runtime to exchange implementation of your own method with the one you wish to modify, and call the original implementation where appropriate. – Léo Natan Sep 03 '13 at 06:38
  • 4
    @LeoNatan No, *no*, **no**.... ***Swizzling is not the answer.*** If your app swizzles system methods, that is against app store policy. And for good reason as it typically leads to fragile, crashy, code that may exhibit new, entirely undefined, behavior on any given platform or update. – bbum Sep 03 '13 at 16:23
  • Please don't tell me what my code "typically" leads, as that is not the case. And please point me to app store "policy" where it is against. The Objective C runtime is public API for a reason, and until people embrace it to its fullest (including ISA swizzling a UIWebDocumentView. ;), they are not using the entire feature set give to them. And no one has appointed you to decide what solution fits where. – Léo Natan Sep 03 '13 at 19:43
  • ***Swizzling is sometimes the answer.*** – Léo Natan Sep 03 '13 at 19:43
  • 5
    @LeoNatan No, really, swizzling methods in system classes is fragile and will cause crashes and/or undefined behavior. While calling the original implementation somewhere in the swizzled implementation certainly mitigates the risk, it still changes the system provided implementation in ways that breaks encapsulation and leads to fragility. Over the 20+ years that I've been writing, maintaining & debugging large scale Objective-C applications, I'm quite confident in the claim that method swizzling is an entertaining hack that should not be used in production code. – bbum Sep 03 '13 at 21:33
  • See this question: http://stackoverflow.com/questions/8834294/app-store-method-swizzling-legality as it contains an example of an app store rejection due to swizzling of system APIs. – bbum Sep 03 '13 at 21:34
  • Seems like a capricious rejection. I will agree that swizzling the dealloc method is probably doing it wrong, I have to admit I have swizzled .cxx_destruct to debug peculiar issues. I will strongly disagree about "entertaining hack". The Objective C runtime as a whole is there to be used. Whatmore, core concepts of Foundation and Cocoa use method and isa swizzling to provide KVO, Appearance proxy, Core Data functionality and so on. – Léo Natan Sep 04 '13 at 03:32
  • 3
    @LeoNatan You are free to use swizzling or whatever mechanisms you want to implement your own functionality. Modifying system provided classes using the same mechanisms is asking for trouble (and rejection, as is evidenced by that question). Simply because a tool can serve a particular function does not mean it is appropriate to use a tool for that function; I can drive a screw with a hammer, but my resulting cabinets are going to be awful. – bbum Sep 04 '13 at 05:31
  • @bbum I would argue you do not know how to use the hammer then. And what is "system provided classes"? Foundation and Cocoa? Nothing system about them, just frameworks provided by Apple, which do similar "trickery" (very misleading word, because there is nothing trick here) with both "system provided" and "user provided" classes and method. – Léo Natan Sep 04 '13 at 13:52
  • 5
    @LeoNatan From the docs: "Native apps are built using the iOS system frameworks and Objective-C language." So, yes, Foundation/Cocoa/UIKit/CoreData (and all the rest of the frameworks delivered by Apple) are "System Frameworks" and your code should not be modifying their behavior beyond what is available via that Framework's public API. Swizzling is no different than mucking about with iVars directly; it breaks encapsulation, is fragile, and leads to crashy apps. To inject a bit of humor, a note left by the UIKit team: http://openradar.appspot.com/7044974 – bbum Sep 04 '13 at 23:51
  • @bbum So it's OK for "system frameworks" to break encapsulation, fragile, lead to crashy apps? Sorry, not sure in what regard you hold Apple developers, but they are people too and can have bugs. From your dramatic statements, sounds like the Objective C framework is the next "goto". Please. Fear of bugs and crashes should not scare you from viable options, it should drive you to have a better code. – Léo Natan Sep 05 '13 at 01:54
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/36905/discussion-between-itai-ferber-and-leo-natan) – Itai Ferber Sep 06 '13 at 07:06
  • @LeoNatan Actually, Dave and bbum have nothing to do with calling you a troll --- I posted a rant with responses to many of your points, and said that I normally avoid responding to "trolls and other people online who bother me"; you're not a troll, just your method of responding to people irked me. Here's my rant, again, with counterpoints to what you'd said: http://pastebin.com/eNaLZnsE. Clearly this discussion is off-topic (which is why all the comments were deleted), but I just don't want you to think either Dave or bbum would stoop that low. – Itai Ferber Sep 06 '13 at 15:34
  • 1
    @ItaiFerber Toda for making it more clear. I apologize again to Dave and bbum. Your rant is actually much more constructive than what I saw in the preview in the mail I got. I would love to answer and have a debate (certain things I agree with and certain I don't), but since this discussion has been deemed off-topic, sadly will not. I will just say, on-topic, that swizzling can, in some cases, be safer than categories, which don't get frowned on as much, but have much larger potential to cause issues, especially with unexposed methods having the same name. – Léo Natan Sep 06 '13 at 15:42
  • @LeoNatan Sure thing — sorry for posting such an angry rant; you likely didn't deserve it. Anyway, yes, there are cases where swizzling is safer than categories, but those cases are usually far and few between. For the most part, if you follow good naming guidelines (putting prefixes on method names in categories), you'll likely never run into naming conflicts. In any case, שנה טובה ומתוקה! ;) – Itai Ferber Sep 06 '13 at 15:49
1

From what I test

  1. Given a superclass category method and a regular method in the subclass, is it guaranteed that the subclass implementation will win when called on a member of the subclass? => subclass wins
  2. Given a superclass regular method and a subclass category method trying to override it, is it guaranteed that the subclass category implementation will win when called on a member of the subclass? => subclass category wins
  3. Given a superclass category method and a subclass category method, is it guaranteed that the subclass category method will win when called on a member of the subclass? => subclass category wins

Take a look at the Test category and subclass

onmyway133
  • 45,645
  • 31
  • 257
  • 263
  • talking about subclasses when it comes to category overwriting seems to be wrong to me already. trust bbum, he is possible one of the most competent persons on stack overflow for this topic. – vikingosegundo May 20 '14 at 04:42
  • @vikingosegundo you're right. I just want to clarify this with a small test. There must be something with the order in which the runtime adds category methods – onmyway133 May 20 '14 at 04:45
  • like bbum says: many classes are devided in categories. we don't know the order they are loaded. – vikingosegundo May 20 '14 at 04:47
  • @vikingosegundo so if we KNOW that methods are implemented in the class itself, we CAN surely override them with category, right ? – onmyway133 May 20 '14 at 04:50
  • as bbum pointed out: you must recreate every behavior of the system class' method you overwrite as you cannot just call the other implementation. that includes implementation details you do not now. if you start to overwrite your own method that is a string indication you have flaws in your design. – vikingosegundo May 20 '14 at 05:03