5

让我们考虑一个具有高度自定义或复杂视图的应用程序。

我们将有一种特定类型的视图控制器向特定类型的 UIView 发送方法,其中 UIView 本身由许多其他视图组成。

视图应该有一个丰富的、特定于域的接口,允许控制器行动是它和一个类似丰富的模型之间的薄“胶水”层。

所以我们重写控制器的视图属性如下:

@interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
    NSMutableArray* _sections;
    LightingMode _lightingMode;
}

@property (nonatomic, strong) PlaybackView* view; // <------ Specific type of view

#pragma mark - injected
@property (nonatomic, strong) id<OscClient> oscClient;
@property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;

@end

覆盖比定义另一个访问器更有意义,我可以将消息发送到特定类型的 UIView 而无需强制转换。

问题:唯一的问题是它会导致编译器警告:

属性类型“PlaybackView *”与从“UIViewController”继承的“UIView *”类型不兼容

. . 我喜欢构建没有任何警告的代码。这样,一个有效的警告就不会被埋在其他警告中而错过。

问题:

  • 有没有办法抑制这个特定的警告?
  • 为什么这部分是默认设置,而大多数现代 OO 语言将很高兴地允许覆盖子类中的属性或方法,以便它返回在超类中声明的类型的更具体的子类?
4

5 回答 5

8

这里的问题不是属性的覆盖,而是使用类类型的前向声明。

所以这...

@class PlaybackView;

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

会给你提到的警告,因为编译器无法知道PlaybackView. UIViewController有合同UIView从其view财产中提供

它告诉你它认为PlaybackView不是UIView

这里的简单解决方案是使用 a#import来让编译器充分了解PlaybackView...

#import "PlaybackView.h"

@interface PlaybackViewController : UIViewController

@property (nonatomic, strong) PlaybackView* view;

@end

或者(但非常糟糕的形式,因为 PCH 是一个优化功能,不应该管理依赖项)是添加#import "PlaybackView.h"到您的项目 PCH

于 2014-05-05T13:51:05.317 回答
1

正如另一个答案中所建议的那样,使用#import而不是@class将清除警告,但建议在标题中尽可能少地导入,所以我建议保持view不变并增加一个PlaybackView * playbackView

  • 拥有两者viewplaybackView指向同一个视图是完全可以的。
  • 需要了解您的专业知识的类必须view导入您的控制器标头,以便它们可以playbackView首先使用。
  • 更重要的是,如果您希望将来将您的专用视图嵌入为子视图(这通常像添加UIScrollView超级视图一样发生),您将不必重构其他代码和类!
  • 很干净。
于 2014-05-06T01:04:36.510 回答
0

我不认为覆盖 UIViewControllers 视图属性是一个好方法。

我认为这样做更好:

@interface PlaybackViewController : UIViewController<StageLayoutDelegate, ControlPanelDelegate>
{
    NSMutableArray* _sections;
    LightingMode _lightingMode;
}

//@property (nonatomic, strong) PlaybackView* view; //you do not need this property

#pragma mark - injected
@property (nonatomic, strong) id<OscClient> oscClient;
@property (nonatomic, strong) AbstractStageLayoutView* stageLayoutView;

@end

并在 .m 文件中。

- (void)loadView
{
    PlaybackView *mainView = [[PlaybackView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
    // set the mainView
    self.view = mainView;
}

你可以像这样使用你的 PlaybackView 。

((PlaybackView *)(self.view)).oscClient

或者

((PlaybackView *)(xxxViewControler.view)).oscClient
于 2013-02-05T02:21:17.267 回答
0

[更新]
最后我可能找到了适合该问题的解决方案:这是一个快速而肮脏的方法,只是为了抑制警告,尝试在这些行之间包装你的代码

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu" 
//YOUR CODE
#pragma clang diagnostic pop

或 -Wall 查看更多关于编译器警告抑制的Clang 手册

[旧答案]
我想给我的 2 美分。如果我理解得很好,您正在尝试创建一种抽象工厂,它为您提供基于视图控制器功能的专用视图版本。在我看来,故事板在这种设计中效果不佳,但我想告诉你我对它的看法。

首先,我将为您的视图控制器创建一个抽象类,在接口中您声明所有 VC 子类中需要的所有属性,例如:

  • 操作系统客户端
  • AbstractStageLayoutView
  • PlaybackView
  • 播放属性

PlaybackView 类是一个类集群,例如 NSNumber,您在其上调用工厂方法,该类将返回一个可能因情况而异的对象。如果你检查一个 NSNumber,如果你创建一个浮点数或一个整数,它会返回一个不同的对象,但它们都是 NSNumber 的子类,并且 NSNumber 声明了它的子类的所有属性,但它没有实现它们。
现在你可以在抽象类的 -viewDidLoad 方法中做的是调用这样的方法

PlaybackView *plbackView = [PlaybackView playbackViewFroProperty:self.playbackProperty];
[self.view addSubview:playbackView];
self.playbackView = plbackView;

PlaybackProperty 可以User defined runtime attibute在 viewcontroller 故事板编辑器的内部赋值。

于 2014-05-05T09:59:33.963 回答
0

从某种意义上说,也许您可​​以声明另一种为您提供演员表的方法。

@implementation PlaybackViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //  use view_ property instead of view
    self.view_.foo = 1;
}

- (void)loadView {
    CGRect frame = [UIScreen mainScreen].applicationFrame;
    self.view = [[PlaybackView alloc] initWithFrame:frame];
}

- (PlaybackView *)view_ {
    return (PlaybackView *)self.view;
}

不完全是最干净的方法,但它确实避免了强制转换self.view(虽然不使用self.view

于 2014-05-05T10:45:21.433 回答