Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to hint use of UIView subclass for compiler

I have a subclass of UIViewController that is responsible for a single UIWebView.

Since this is a simple case, I override -(void)loadView, instantiate the UIWebView and assigning it the controller's view property:

- (void)loadView 
{
    UIWebView *wv = [[[UIWebView alloc] initWithFrame:self.frame] autorelease];
    // other configuration here...  
    self.view = wv;
}

This is fine until I call a method of UIWebView's. For example...

[self.view loadHTMLString:HTMLString baseURL:baseURL];

...leads to a compiler warning...

warning: 'UIView' may not respond to '-loadHTMLString:baseURL:'

...since the view property is declared as UIView.

NOW the warning is easily solved with a cast...

[(UIWebView *)self.view loadHTMLString:HTMLString baseURL:baseURL];

...but what I'd like to do is provide the correct type hint in the interface. I tried overriding the view property in MyViewController.h but this upsets the compiler too:

warning: property 'view' type does not match super class 'UIViewController' property type

Is there any way of telling the compiler (and my fellows) that this is what I'm doing, and that I know that this is what I'm doing and that it's all okay? (If not I guess I'll stick with the cast.)

TIA

EDIT: I tried redeclaring the view property as per marcus.ramsden's answer: this eliminated the warning (and the need for the cast) but stopped the view appearing at all! I'm not sure why this should be as the controller will still return a UIView (subclass) when asked for it...

like image 746
Carlton Gibson Avatar asked Feb 25 '23 18:02

Carlton Gibson


2 Answers

The short answer is there's no reasonable way to do this. And finding an unreasonable way to do it will create more problems than it will solve. I realize this isn't the answer you want, but for the benefit of anyone who reads this question in the future I'll explain why I believe you shouldn't do this.

The view property is part of the public interface of UIViewController, and the type of the return value is part of that interface. Changing the public interface of a class which you've subclassed is smelly. If that's not reason enough for you, consider that the view property of a view controller is a fundamental bit of UIKit; there are few things you could mess with that could potentially have a more systemic effect on the framework. Who knows what sort of dark machinations lie under the façade of that simple interface getter? Based on some of the comments already left here it seeems that redefining this property breaks things in obvious ways; I would stay away.

If you come from a language like ruby, the fact that you can't send an arbitrary message to the object returned by the view property without a compiler warning probably seems unsatisfying. However, for better or for worse, Objective C is a statically (if not particularly strongly) typed language. When you're working with a particular tool it's worth trying to work to that tool's strengths, and avoid its weaknesses, rather than trying to shoehorn it into another tool's idioms.

If, on the other hand, you come from a strongly typed language like C++, that cast should feel extremely unsatisfactory as well. Casts subvert the type system that statically typed languages lean heavily on, and are nearly always a nasty code smell. C-style casts are the nuclear weapon of the C type system; they override absolutely everything, and they are nearly always the wrong thing to use.

What if you decide in the future that you want to add a label to your view along with the web view? In that case you'll need to move the web view to be a subview; but if you're casting the view controller's base view to UIWebView everywhere, then your casts are now lies. Sadly, the compiler won't help you find those lies, because C-style casts are unchecked; you can make a UIWebView out of a UIView, an int, a block, a function pointer, or a rhinocerous and the compiler won't make a peep. Your app will crash mightily, though.

Finally, you say you're interested in avoiding complexity. Consider that adding your web view as a subview of the base view would require only that you declare (and synthesize) a single property and add the subview in viewDidLoad. Given that small amount of effort, all calls to your web view look like this:

[self.webView loadHTMLString:HTMLString baseURL:baseURL];

Alternately, if you stick with casting, each call to your web view will look like this:

[(UIWebView *)self.view loadHTMLString:HTMLString baseURL:baseURL];

The former seems less complex to me, and less brittle.

like image 181
Adam Milligan Avatar answered Mar 09 '23 11:03

Adam Milligan


Since it's a subclass, you can override the view property/method to be a UIWebView. Since a UIWebView is a subclass of UIView, and you're the only user of your subclass, this should be completely safe.

@interface MyAbstractViewController : UIViewController {
    UIWebView *_webView;
}
@property (nonatomic, retain) UIWebView *view;
@end

@implementation MyAbstractViewController    
@dynamic view;
- (void)setView:(UIWebView *)webView {
  [super setView:webView];
  _webView = webView;
}

- (UIWebView *)view {
  return _webView;
}
@end
like image 20
August Lilleaas Avatar answered Mar 09 '23 11:03

August Lilleaas