如何在iPhone上在UIViewControllers之间共享对象?

23
我的应用程序是一个选项卡式应用程序,每个选项卡都有一个单独的视图控制器。
在第一个视图控制器(A)中有一个对象,其中包含所有存储的应用程序数据(请忽略NSUserDefaults),需要在按下第二个视图控制器(B)上的按钮时由其访问。如何以最佳方式实现这一点?

这个问题非常类似于我之前提出的一个问题:http://stackoverflow.com/questions/1053704/organizing-instance-variables-in-a-view-hierarchy - titaniumdecoy
2
没错。你不能因为我没有找到它而责怪我。它的措辞完全不同。 - Brock Woolf
5个回答

31

你可以选择将日期模型声明为应用程序委托的实例变量(正如其他评论者所提到的)。

与 nevan 建议的引用应用程序委托不同,另一种选择是为您的视图控制器类(A 和 B)添加一个数据模型属性。

假设您想在视图控制器之间共享数据模型对象,您可以为每个视图控制器添加一个属性:

@interface AViewController : UIViewController {
    MyDataModel *model;
}

@property (nonatomic, retain) MyDataModel *model;

@end

@interface BViewController : UIViewController {
    MyDataModel *model;
}

@property (nonatomic, retain) MyDataModel *model;

@end

当您初始化视图控制器时,可以将此属性设置为之前初始化的对象上下文。

您提到了选项卡栏控制器。如果您的视图控制器通过IB连接,那么您只需要在选项卡栏控制器显示之前,在应用程序委托的applicationDidFinishLaunching:方法中设置这些参数:

@interface MyAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate>
{

    MyDataModel *model;
    AViewController *aViewController;
    BViewController *bViewController;
    ...
}

@property (retain) IBOutlet AViewController *aViewController;
@property (retain) IBOutlet BViewController *aViewController;

@end

@implementation MyAppDelegate

...

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
...

    aViewController.model = model;

    bViewController.model = model;

    [window addSubview:tabBarController.view];
    [window makeKeyAndVisible];
}

别忘了在你的视图控制器的dealloc方法中释放模型。


另一种方法是使用单例对象。一个简单的单例示例:

@interface MyDataModel : NSObject
{
}

+ (MyDataModel *) sharedDataModel;

@end

@implementation MyDataModel

static MyDataModel *sharedDataModel = nil;

+ (MyDataModel *) sharedDataModel
{

    @synchronized(self)
    {
        if (sharedDataModel == nil)
        {
            sharedDataModel = [[MyDataModel alloc] init];
        }
    }
    return sharedDataModel;
}

@end

您可以通过类似以下的方式从所有视图控制器访问此数据模型:

MyDataModel *model = [MyDataModel sharedDataModel];

请参见堆栈溢出讨论关于单例模式。


1
我真的很喜欢这个答案。使用指针从AppDelegate引用数据模型似乎比使用Singleton更加清晰。这种方法还可以将耦合降至最低。谢谢。 - Brock Woolf
现在看来我本来会使用这两种方法之一。但是为了让我感到困惑,斯坦福第七课说这两种方法都不好!请查看:http://deimos3.apple.com/WebObjects/Core.woa/Feed/itunes.stanford.edu.3124430053.03124430055 大约在30分钟左右。无论如何,谢谢你,我将使用单例:P - jowie
看完您所提到的第七讲,我相信他(Josh Shaffer)在这里倡导第一种方法。您绝对是正确的,他不建议使用单例模式,因为它只是另一种全局状态。当他建议不要使用应用程序委托时,我认为他指的是静态访问应用程序委托并通过其获取数据。他的示例表明,我认为非常接近szzsolt上面提到的第一种方法。即仅传递您的视图控制器需要的内容。 - greggian

8

我见过最常用的做法是在应用程序委托中设置你想要访问的内容,然后在其他地方引用它,就像这样:

MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
myStuff = appDelegate.stuff;

在应用程序委托中,设置一个stuff变量并像往常一样使用@property和@synthesize。有些人认为这不是一个好的方法,因为它与使用全局变量相同,但这是非常普遍的做法。

1
使用单例模式也是一种常见的方法。 - titaniumdecoy
我真的不明白为什么被接受的答案更好。我之前广泛使用了类似的方法,但它只是给我带来了额外的复杂性。来回传递对象只会让人头疼。+1 - Tibidabo
1
@Tibidabo:因为从面向对象编程和代码可重用性的角度来看,将数据保存在'appDelegate'中被认为是一种非常糟糕的做法。否则,作为一个快速而简单的解决方案,这个答案是完全可以接受的。 - Rok Jarc

5

我喜欢创建一个顶层的Model类,作为单例模式,并包含我可能需要的所有元素。

同时为其提供一个顶级的load方法,使用在Apple示例中常见的hydrate/dehydrate模式,仅用db keys填充对象。

在应用程序委托中的典型用法只需简单地:

[[MyModel sharedModel] load];

然后在视图控制器中:

NSArray *myThing1s = [[MyModel sharedModel] thing1s];
NSArray *myThing2s = [[MyModel sharedModel] thing2s];

您可以遍历thing1和thing2,当您需要详细信息时,只需调用即可。
[myThing1 hydrate];

这将填充该对象。

当然,从3.0开始,您可能希望使用CoreData来管理持久性。


你能否在Objective-C中发布一个我可以使用的Singleton示例?我倾向于这种方式,它似乎比Delegate访问更好一些。 - Brock Woolf

3

我经常创建一个特殊的对象称为 DataModel,并使用它的单例 sharedInstance

然后这个对象保存所有与应用相关的数据。无需访问 可怕的 appDelegate

DataModel.h

#import <Foundation/Foundation.h>

@class MyClass1, MyClass2;

@interface DataModel : NSObject

@property (copy, nonatomic) NSString *aString;
@property (assign) BOOL aBool;

@property (strong) MyClass1 *myObject1;
@property (strong) MyClass2 *myObject2;

+ (DataModel *)sharedModel;

@end

DataModel.m

#import "DataModel.h"
#import "Class1.h"
#import "Class2.h"

@implementation DataModel

- (id) init
{
    self = [super init];
    if (self)
    {
        _myObject1 = [[MyClass1 alloc] init];
        _myObject2 = [[MyClass2 alloc] init];
        aBool = NO;
        aString = nil;
    }
    return self;
}

+ (DataModel *)sharedModel
{
    static DataModel *_sharedModel = nil;
    static dispatch_once_t onceSecurePredicate;
    dispatch_once(&onceSecurePredicate,^
                  {
                      _sharedModel = [[self alloc] init];
                  });

    return _sharedModel;
}

@end

因为我比较懒,所以把DataModel.h放在application-prefix.pch中。

这样一来,我只需调用一次就可以从应用程序的任何地方访问我的数据了。

[DataModel sharedModel]

1
我曾经使用过这种方法,取得了巨大的成功。 - David Carrico

0

两个视图控制器都应该引用第三个对象(C)作为它们的数据源;这个对象(C)包含了所有存储的应用程序数据。

在这种情况下,C将是MVC中的M。

向每个您的视图控制器添加以下声明:

// SomeViewController.h
// Before @interface

@class MyDataSource;

// In the interface

IBOutlet MyDataSource *datasource;
@property(retain) IBOutlet MyDataSource *datasource;

没错。那么我该怎么做呢?我应该在哪里存储我的数据模型,如何以正确的方式访问它,以便从两个视图控制器中都遵守MVC。 - Brock Woolf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接