Как намекнуть использование подкласса UIView для компилятора - PullRequest
4 голосов
/ 23 ноября 2010

У меня есть подкласс UIViewController, который отвечает за один UIWebView.

Поскольку это простой случай, я переопределяю -(void)loadView, создаю экземпляр UIWebView и присваиваю ему свойство view контроллера:

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

Это нормально, пока я не вызову методUIWebView годов.Например ...

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

... приводит к предупреждению компилятора ...

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

..., поскольку свойство view объявлено как UIView.

СЕЙЧАС предупреждение легко решается с помощью приведения ...

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

... но я хотел бы предоставить правильную подсказку типав интерфейсе.Я попытался переопределить свойство view в MyViewController.h, но это также расстраивает компилятор:

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

Есть ли способ сказать компилятору (и моим товарищам), что это то, что я делаю,и что я знаю, что это то, что я делаю, и что все в порядке?(Если нет, я думаю, что я буду придерживаться актерского состава.)

TIA

РЕДАКТИРОВАТЬ: Я попытался переопределить свойство представления в соответствии с ответом marcus.ramsden: это устраненопредупреждение (и необходимость в приведении), но остановило представление, появляющееся вообще!Я не уверен, почему так должно быть, поскольку контроллер по-прежнему будет возвращать UIView (подкласс), когда его об этом попросят ...

Ответы [ 6 ]

5 голосов
/ 30 ноября 2010

Краткий ответ: нет разумного способа сделать это. А поиск неразумного способа сделать это создаст больше проблем, чем решит. Я понимаю, что это не тот ответ, который вам нужен, но в интересах любого, кто будет читать этот вопрос в будущем, я объясню, почему я считаю, что вы не должны этого делать.

Свойство view является частью открытого интерфейса UIViewController, а тип возвращаемого значения является частью этого интерфейса. Изменение открытого интерфейса класса, который вы вложили в подкласс, вонючий. Если вам этого недостаточно, подумайте, что свойство view контроллера представления является фундаментальным элементом UIKit; Есть несколько вещей, с которыми вы могли бы связываться, которые потенциально могли бы иметь более системный эффект на платформе. Кто знает, что за темные махинации лежат под фасадом этого простого интерфейсного геттера? Основываясь на некоторых комментариях, уже оставленных здесь, кажется, что переопределение этого свойства ломает вещи очевидным образом; Я бы держался подальше.

Если вы работаете с таким языком, как ruby, тот факт, что вы не можете отправить произвольное сообщение объекту, возвращаемому свойством view без предупреждения компилятора, может показаться неудовлетворительным. Однако, к лучшему или к худшему, Objective C - это статически (если не особенно сильно) типизированный язык. Когда вы работаете с конкретным инструментом, стоит попытаться использовать его сильные стороны и избегать его слабостей, а не пытаться использовать его в идиомах другого инструмента.

Если, с другой стороны, вы пришли из строго типизированного языка, такого как C ++, то приведение также должно быть крайне неудовлетворительным. Приведения приводят к разрушению системы типов, на которую опираются статически типизированные языки, и почти всегда имеют неприятный запах кода. Слепки в стиле C являются ядерным оружием системы типов C; они перекрывают абсолютно все, и их почти всегда нельзя использовать.

Что если в будущем вы решите добавить метку к своему представлению наряду с веб-представлением? В этом случае вам нужно переместить веб-представление в подпредставление; но если вы повсеместно преобразуете базовое представление контроллера представления в UIWebView, то ваши приведения теперь ложные. К сожалению, компилятор не поможет вам найти эту ложь, потому что приведения в стиле C не проверяются; вы можете сделать UIWebView из UIView, типа int, блока, указателя функции или носорога, и компилятор не будет пискать. Тем не менее, ваше приложение будет сильно зависать.

Наконец, вы говорите, что хотите избежать сложности. Учтите, что добавление веб-представления в качестве подпредставления базового представления потребует только того, чтобы вы объявили (и синтезировали) одно свойство и добавили подпредставление в viewDidLoad. Учитывая это небольшое усилие, все вызовы в вашем веб-представлении выглядят так:

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

С другой стороны, если вы продолжаете кастинг, каждый вызов вашего веб-представления будет выглядеть следующим образом:

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

Первое кажется мне менее сложным и менее хрупким.

2 голосов
/ 03 декабря 2010

Вы можете определить свойство

@property (nonatomic, readonly) UIWebView *webView;

и сделайте это:

- (UIWebView*)webView {
   return (UIWebView*)self.view;
}

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

Затем вы можете получить доступ к вашему WebView впоследствии self.webView без приведения. По сути, вы централизуете приведение к одному конкретному месту в вашем коде. Если вы когда-нибудь захотите вставить другое представление, например метку, вам просто нужно изменить одно место в вашем коде.

2 голосов
/ 30 ноября 2010

Поскольку это подкласс, вы можете переопределить свойство / метод view на UIWebView. Поскольку UIWebView является подклассом UIView, и вы единственный пользователь своего подкласса, это должно быть абсолютно безопасно.

@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
0 голосов
/ 30 ноября 2010

Как уже упоминалось, хотя возможно сделать это, это может быть или не быть мудрым .

Вы можете объявить категорию на самом UIView., предполагая, что это может ответить на селекторы, которые вы пытаетесь вызвать.Это можно сделать в файле реализации вашего пользовательского контроллера представления.Это выглядело бы примерно так:

@interface UIView (WebViewFakery)

- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL

@end

Вам нужно было бы добавить все методы UIWebView, которые вы пытаетесь вызвать на view.Возможно, не самое элегантное решение, но другое, которое стоит рассмотреть.

0 голосов
/ 30 ноября 2010

Приведите значение или добавьте новую переменную экземпляра UIWebView в свой viewController.

Это не работа метода представления UIViewController, чтобы сообщить своим товарищам, какой подкласс вы использовали.

Если бы вы могли делать то, что хотели, вам просто нужно было бы вернуться к UIView во всех местах, где UIKit ожидает, что метод представления UIViewController вернет UIView.

0 голосов
/ 24 ноября 2010

Из документации видно, что можно делать то, что вы хотите, используя категории. Например;

// UIWebViewController.h
@interface UIWebViewController : UIViewController {

}

@end

@interface UIWebViewController ()

@property (nonatomic,retain) UIWebView *view;

@end

// UIViewController.m
@implementation UIWebViewController

@synthesize view;

- (void)viewDidLoad {
    UIWebView *wv = [[UIWebView alloc] initWithFrame:webViewFrame];
    self.view = wv;
    [self.view loadHTML:@"<h1>Hello World</h1>" baseURL:baseURL];
    [wv release];
}

@end

Немного об этом есть в http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html в Объявленных свойствах> Использование свойств> Повторное объявление свойств.

Попробовал это, и я не получил никаких жалоб компилятора.

...