3

我在我的故事板中设置了这个 UIViewController,其中包含我需要的所有插座、视图和约束。完美的。我们称之为 WatchStateController,它将作为一个抽象父类。

然后,我有了 WatchStateController 的这个子类,称为 WatchStateTimeController,它将具有应用程序特定状态所需的功能。

因为我试图在 UIStoryboard 中使用 1 个视图控制器,所以在将 WatchStateTimeController 实例化为 WatchStateTimeController 类型时遇到了一些问题 - 它实例化为 WatchStateController。

UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];

WatchStateTimeController *timeController = (WatchStateTimeController *)[mainStoryboard instantiateViewControllerWithIdentifier:@"WatchStateController"];

这是因为情节提要的 Identity Inspector 中的“Class”字段设置为“WatchStateController”。所以问题是,我如何仅在运行时更改 Identity Inspector 中设置的这个类名?

身份检查员

注意:忽略我为什么要这样做并专注于如何. 如果你真的必须知道为什么,你可以阅读策略设计模式。

4

2 回答 2

6

我用来强制情节提要与策略模式兼容的一个稍微肮脏的解决方法:我在基础(抽象)视图控制器中编写了一个自定义分配器,它在情节提要机制结束之前返回所需的具体视图控制器子类的实例.

为此,您必须告诉基类您要实例化哪个子类。

因此,在基本控制器中:

Class _concreteSubclass = nil;
+ (void) setConcreteSubclassToInstantiate:(Class)c {
    _concreteSubclass = c;
}

+ (id)allocWithZone: (NSZone *)zone {
    Class c = _concreteSubclass ?: [self class];
    void *object = calloc(class_getInstanceSize(c), 1);
    *(Class *)object = c;
    return (id)CFBridgingRelease(object);
}

这也为子类的 ivars 实例化了足够的内存。

故事板已知的“MyViewController”视图控制器类型只是“BaseViewController”;但是,当您要求故事板实例化视图控制器时,您可以执行以下操作:

[BaseViewController setConcreteSubclassToInstantiate:[SomeSubclassOfBaseViewController class]];
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil];
SomeSubclassOfBaseViewController *vc = (SomeSubclassOfBaseViewController *)[mainStoryboard instantiateViewControllerWithIdentifier:@"MyViewController"];
[self presentViewController:vc animated:NO completion:^{}];

具体的视图控制器被实例化并顺利显示。

于 2014-11-23T11:57:09.343 回答
5

这是一个使用辅助对象的策略模式示例,正如我在评论中所描述的:

@class WatchStateController;

@protocol WatchStateStrategy <NSObject>
- (void)doSomeBehaviorPolymorphically:(WatchStateController *)controller;
@end

@interface WatchStateController
// or call this a delegate or whatever makes sense.
@property (nonatomic) id <WatchStateStrategy> strategy;
@end

@implementation WatchStateController
- (void)someAction:(id)sender
{
    [self.strategy doSomeBehaviorPolymorphically:self];
}
@end

@interface WatchStateTimeStrategy <WatchStateStrategy>
@end

@implementation WatchStateTimeStrategy
- (void)doSomeBehaviorPolymorphically:(WatchStateController *)controller
{
    // here's one variation of the behavior
}
@end

@interface WatchStateAnotherStrategy <WatchStateStrategy>
@end

@implementation WatchStateAnotherStrategy
- (void)doSomeBehaviorPolymorphically:(WatchStateController *)controller
{
    // here's another variation of the behavior
}
@end

并且要在展示视图控制器时进行设置,请分配适当的辅助对象(而不是尝试更改视图控制器本身的子类):

WatchStateController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"WatchStateController"];
if (useTimeStrategy) {
    viewController.strategy = [WatchStateTimeStrategy new];
} else {
    viewController.strategy = [WatchStateAnotherStrategy new];
}

与子类化视图控制器相比,我看到这种方法的优势:

  • 它更符合SOLID 原则,尤其是单一责任原则、开放/封闭原则等。
  • 如果您打算编写测试,小型、集中的帮助类,可能很少或没有 UI 依赖项,具体取决于他们需要做什么,这使得单元测试更容易
  • 它更紧密地遵循 iOS 中已经存在的设计模式和结构模式(使用委托,并让故事板/xibs 以正常方式实例化视图控制器)
  • 从视图控制器中删除逻辑。在 iOS 中,很容易得到一个包含太多逻辑的大型视图控制器;我认为我们应该一直寻找机会来改善这一点
于 2013-06-29T15:48:52.647 回答