2

Suppose you have a UIView subclass. You define an init method "myInitWithFrame: ... andWhatNot:...". You know you won't be using the init method inherited from UIView ever and your custom init method does some vital custom initialising so that you want to force client classes to never use the inherited initWithFrame method.

Is it possible to hide the standard initWithFrame method that was inherited from UIView?

Jeff Wolski
  • 6,332
  • 6
  • 37
  • 69
Matt N.
  • 1,239
  • 2
  • 11
  • 26

3 Answers3

20

Actually, you can get compile-time warnings about calling a method on a subclass. Use the __attribute((deprecated)) attribute. If you want people to use -initWithPizza: instead of -initWithFrame:, do this:

@interface MyView : UIView
- (id)initWithPizza:(MyPizza *)pizza;
@end

@interface MyView (Deprecations)
- (id)initWithFrame:(CGRect)frame __attribute((deprecated("Use initWithPizza: instead")));
@end

Putting the -initWithFrame: declaration in a separate category is necessary to avoid Xcode complaining that you declared the method in the header but didn't implement it. Since you're just inheriting it from the superclass, that's fine; you don't have to implement it at all. But if you want to implement it to throw an exception, or call through to -initWithPizza: with a default argument, that's fine.

Of course, this won't stop UIKit from calling -initWithFrame: if it was already going to do so. But if you can guarantee that won't happen, then you're fine.

BJ Homer
  • 48,806
  • 11
  • 116
  • 129
  • Thank you! Especially for posting it after I had already accepted another answer. This is great! – Matt N. Jun 01 '13 at 06:16
  • If I try to put these two things into my view's header file, it will not compile. It claims that the attribute has the wrong number of arguments. I can "fix" it and make it compile by using `- (id)initWithFrame:(CGRect)frame __attribute__((deprecated));` instead. Then it compiles but without any warnings even though I'm calling this method somewhere else in order to raise the compiler warning. – Matt N. Jun 04 '13 at 08:23
  • Hm. Are you using the latest version of Xcode? I'm using Xcode 4.6.2, and the code I provided compiles and provides warnings when I call it. – BJ Homer Jun 04 '13 at 16:04
  • Sorry, no, I am not. : ) – Matt N. Jun 04 '13 at 16:07
7

Actually, you CAN restrict with a subclass. You can override whichever methods you want to block in your subclass's .h file. You can make initWithFrame unavailable by placing the following in your .h file.

- (id) initWithFrame:(CGRect) frame __attribute__((unavailable("message")));

This will make the initWithFrame: method unavailable to anyone using your subclass.

To keep other code form calling this method, you can further restrict by putting this in your .m file.

- (id) initWithFrame:(CGRect) frame
{
    return nil;     
}
Jano
  • 62,815
  • 21
  • 164
  • 192
Jeff Wolski
  • 6,332
  • 6
  • 37
  • 69
  • This doesn't seem to compile. But if I remove the argument it does. Adding your second suggestion into the class yields a compiler warning "Method attribute may be specified on method declaration only". But even so: I find your answer pretty useful and se nothing wrong with upvoting you! – Matt N. Jun 01 '13 at 06:26
  • 1
    Perhaps you could edit your answer and modify the first bit of code so as to compile? – Matt N. Jun 01 '13 at 06:27
  • Don't add the attribute on the implementation or omit the implementation entirely. – Jano Jun 01 '13 at 10:30
  • I copied and pasted from this answer into Xcode, and it compiles on my machine. Perhaps you could give me the error you are seeing? – Jeff Wolski Jun 01 '13 at 15:44
  • Oh sorry, my second comment should have been ".... so as to compile without errors". T_T – Matt N. Jun 02 '13 at 06:30
3

No. You can't prevent the users of your subclass from calling the methods of a superclass. You could override them and throw an exception inside, but that would just produce a broken subclass.

Remember that inheritance works as an "is a" extension, that is, instances of your subclasses should behave normally in any context that doesn't know about this particular subclass but knows about its superclass. It's only in places that have explicit knowledge about your subclass that you can benefit from adding extra initialization and other methods.

For example, UIKit has no knowledge of your subclass. So if you want to make your UIView subclass available from a NIB, you need to use the initialization methods that will be called by the NIB loading system, namely initWithCoder:. You can simply call your own initialization methods inside initWithCoder:. But if there are any additional parameters you would like to pass to the init method, you'll have to provide a way to configure them after initialization.

Alexei Sholik
  • 7,287
  • 2
  • 31
  • 41
  • Right. I do have parameters I pass that's why I can't call my custom method from within the standard method. Duh, what a dilemma. – Matt N. May 31 '13 at 16:25
  • 1
    Here's a discussion on this topic in a large open-source project - AFNetworking recently decided to throw an exception in `init`: https://github.com/AFNetworking/AFNetworking/pull/1019#issuecomment-18312495 – Aaron Brager May 31 '13 at 16:32