【问题标题】:How can I avoid several instances of the same model object in iOS (MVC)如何避免iOS(MVC)中同一模型对象的多个实例
【发布时间】:2012-02-14 06:59:06
【问题描述】:

我正在从事一个项目,其中我有一个模型被几个不同的视图使用,从而被视图控制器使用。这些视图控制器不知道彼此的存在,彼此之间也没有任何关系。这意味着我在每个视图控制器中都有一个模型*,当加载视图时,我在每个类中分配模型并让指针指向它。或者简而言之:我在 n 个使用它的类中分配我的模型 n 次,我认为这是浪费内存(不是说我会用完内存,但我认为这是不好的做法)。

在 iOS 中有没有一种方法可以让我(同时仍然保持良好的 MVC 实践)能够创建和使用我的模型的相同实例?通常我一直在用 C++ 编程,我会将模型的引用传递给每个应该知道模型的类的构造函数。示例(c++):

// Let to classes know of the same model object
MyModel model;
ControllerA myControllerA(&model);
ControllerB myControllerB(&model);

相反,我在每个使用我的模型(objective-c)的类中执行以下操作:

// ControllerA
model = [[MyModel alloc] init];

// Controller B
model = [[MyModel alloc] init];

我不想让所有模型都成为单例对象,在这个特定的项目中,我认为使用观察者模式会有点过头了。

所以,我的问题是:我怎样才能做到这一点,甚至有可能吗?

【问题讨论】:

    标签: iphone ios model-view-controller model


    【解决方案1】:

    您可以编写自己的初始化程序,它接受指向模型的指针。

    在 ControllerA 和 B 的 .h 文件中

    @property(nonatomic,assign)Model* myModel;
    
    -(id)initWithModel:(Model*)model;
    

    在 ControllerA 和 B 的 .m 文件中

    @synthesize myModel;
    
    -(id)initWithModel:(Model*)model{
        self = [super init];
    
        if(self){
            self.mymodel = model;
    
        }
    
        return self;
    }
    

    编辑

    如果您使用的是 IB,您可以按以下方式编写您的初始化程序:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil model:(Model*)model
    {
        self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
        if (self) {
    
            self.mymodel = model;
        }
        return self;
    }
    

    【讨论】:

    • 好吧,也许我缺少一些东西。如果我确实编写了一个 initWithModel: 方法,那么什么时候应该调用它?在 IB 中,我将 viewA 告诉用户 controllerA,但我没有在任何地方调用任何 init 方法?
    • 根据您构建应用程序的方式,您的 UIViewController(如果它是根控制器)可以在加载时由 AppDelegate 或任何其他 UIViewController(如 UINavigationController)初始化。我不知道你是如何创建你的应用程序的。
    • 好的,我不知道。我有许多视图控制器,它们都是导航控制器的根控制器。
    • @Muncken 请看我为从 IB 初始化的 UIViewController 类显式添加初始化程序的编辑
    • 对不起,忘了说我正在使用情节提要。我想我必须在 prepareForSegue: 方法中调用 init?
    【解决方案2】:

    我认为,你需要使用 singleton 模式

    女巫的简单实现看起来像这样

    YourClass.h

    + (id)sharedInstance;
    

    YourClass.m:

    +(id)singleton {
        static dispatch_once_t pred;
        static MyClass *shared = nil;
    
    
        dispatch_once(&pred, ^{
         shared = [[MyClass alloc] init];
        });
        return shared;
    }
    

    关于它的好文章 apple reference

    【讨论】:

    • 我知道单例,谢谢。问题是我认为将整个模型层设为单例并因此对系统的其余部分是全局的是不好的做法。
    【解决方案3】:

    您的想法是正确的:在创建模型时将模型提供给需要它的视图控制器;不要去寻找单身人士或其他全局对象来获取他们需要的信息。

    由于您询问了视图控制器,并且由于它们通常是从 .xib 或故事板文件实例化的,因此您可能需要稍微调整一下您的方法。无需在初始化程序中提供对模型的引用,您只需向每个视图控制器添加一个存储对模型的引用的属性。然后,负责创建视图控制器的对象可以在创建控制器后提供模型。例如,您的应用代理的-applicationDidFinishLaunchingWithOptions: 方法将在根视图控制器创建后被调用,这是设置模型并将根视图控制器指向它的好时机:

    -applicationDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        // set up the model
        self.model = [[MyModel alloc] initWithFile:...];
    
        // get the root controller and give it a pointer to the model
        MyFirstViewController *firstController = self.window.rootViewController;
        firstController.model = model;
    }
    

    然后根视图控制器可以将模型传递给它创建的其他视图控制器。如果您有一个基于选项卡的应用程序,则应用程序委托可能会将模型传递给选项卡控制器下的所有视图控制器。在这种情况下,您的选项卡控制器将是窗口的根视图控制器,您将像这样访问您的视图控制器:

    NSArray *controllers = self.window.rootViewController.viewControllers;
    

    与单例方法相比,这里最大的优势是您的视图控制器不对应用程序的其余部分进行任何假设。他们每个人都会使用你给他们的任何模型。这使您的代码更简洁、更易于管理、更易于重新排列等等。

    【讨论】:

    • 你完全明白了我的意思。不过我还有一个问题。因为我使用的是 CoreData 和 Storyboard,所以我的 didFinishLaunchingWithOptions: 只返回 YES 并且什么都不做。我确实正在构建一个带有标签栏的应用程序,但我似乎无法从我的 appDelegate 中访问该标签栏控制器。
    • @Muncken 您的应用程序委托应该有一个窗口属性——我认为这是使用情节提要的要求。该窗口有一个 rootViewController 属性,它将被设置为标签栏控制器。然后,您可以获得选项卡控制器管理的各种控制器。我会在上面添加一个sn-p。
    • 谢谢,迦勒。然而,有一件事。本教程raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1 在开头的某处说 didFinishLaunchingWithOptions: 应该按照我上面提到的那样实现。看来你不同意这一点。你能进一步解释一下吗?他们声明在使用情节提要时不再需要,因为在 info.plist 中设置了 UIMainStoryboardFile。
    • 在故事板出现之前,以这种方法进行大量设置更为典型——例如,您必须加载或创建视图控制器。您现在不需要这样做,但您仍然可以将它用于您想要的任何设置任务。另一种方法是让您的根控制器创建模型,但您可以看到在基于选项卡的应用程序中这有多么困难。
    • 我明白了。再次感谢@Caleb,我会选择你的解决方案,这对我来说似乎是最优雅的。
    【解决方案4】:

    创建模型对象后,您可以将这些实例存储在带有键的全局字典中,存储在某个只有一个实例的类中,即单例。当视图控制器需要这个模型时,它可以通过提供一个字典键来请求这个单例给它它需要的数据。

    或者您可以将数据存储在 coredata 中,并在需要时从视图控制器中获取。如果需要,您还可以通过这种方式实现持久性。

    【讨论】:

    • 这引出了一个问题:多个视图控制器如何共享同一个托管对象上下文?
    • 我的做法是模型控制moc。视图控制器从模型请求数据,然后模型创建适当的请求,获取数据并返回。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    • 2021-11-24
    • 2016-01-05
    • 1970-01-01
    相关资源
    最近更新 更多