在两个UIViewControllers之间访问NSMutableArray的正确方法?

时间:2010-02-14 19:32:41

标签: iphone cocoa-touch model-view-controller

设计问题:

我有一个包含NSMutableArray *的ViewController A. ViewController A负责向用户显示Map,当用户与此地图交互时,视图控制器A用坐标对象填充NSMutableArray *。

NSMutableArray *中包含的信息稍后应显示在UITable中,该UITable包含在与ViewController B相关联的另一个视图中。

ViewController B访问由A填充的NSMutableArray的最正确方法是什么? (A保留对NSMutableArray *的引用。)

应该有几种方法可以做到这一点,但为了让自己保持纯粹的MVC,我真的很想知道你的意见。

3 个答案:

答案 0 :(得分:2)

  

最正确的方法是什么?   ViewController B访问   由A?

填充的NSMutableArray

我会做一些简单的事情,如果它引起问题,只会回到决定。简单的事情可能是在控制器A的公共接口中公开数组并发送有关数组更新的通知,以便B可以观察:

@interface A
@property(readonly) NSArray *foos;
@implementation
- (void) updateFoos {
    NSString *const key = @"foos";
    [self willChangeValueForKey:key];
    [foos doSomething];
    [self didChangeValueForKey:key];
}

@interface B
@implementation
- (void) afterSettingA {
    [a addObserver:self forKeyPath:@"foos" options:0 context:NULL];
}
- (void) observeValueForKeyPath: (NSString*) keyPath ofObject: (id) object
    change: (NSDictionary*) change context: (void*) context 
{
    NSAssert([keyPath isEqualToString:@"foos"], @"Somethind fishy going on.");
    // update what depends on foos
}

另一个简单的解决方案是将数组转换为一个完整的模型类,您可以将它们连接到A和B.(连接必须在控制器外部完成,以避免过度耦合。您可以使用Interface Builder,一个'工厂'类,可以将对象连接在一起或任何其他适合的东西。)

@interface Foo
@property(readonly) NSArray* items;
@implementation
- (void) updateItems {
    // send KVO notifications just as above
}

@interface A
@property(retain) Foo *fooModel;

@interface B
@property(retain) Foo *fooModel;

@interface Factory
@implementation
- (void) wireObjects {
    A *a = [[A alloc] init];
    B *b = [[B alloc] init];
    Foo *fooModel = [[Foo alloc] init];
    [a setFooModel:fooModel];
    [b setFooModel:fooModel];
    // Of course the A and B would be member variables of this
    // class or you would return a root of the constructed object
    // graph from this method, otherwise it would not make sense.
}

在第一个解决方案中,B控制器必须有一个指向A的指针,以便它可以订阅KVO通知。控制器之间的这种连接最好保存在别的地方而不是代码中,即。 B不应该创建A的实例。(这样你就会引入A和B之间的紧密耦合。不太可测试等。)如果你已经在Interface Builder中实例化控制器,那么这是给B指针的最佳位置答:(只需为B中的A创建IBOutlet。)

具有单独模型类的第二个解决方案是“更干净”的MVC,并且不需要控制器彼此了解 - 它们都依赖于模型类。您也可以实例化模型并将其链接到Interface Builder中的控制器。

顺便说一句:如果B想要观察A的某些属性的变化,则必须在设置了A的链接后进行订阅。一个简单但非常错误的方法是在B的viewDidLoad方法中订阅它很方便,但是如果之后A的链接发生了变化,则通知不会相应地改变。更难但正确的订阅方式是在A的设置器中 - 当某人设置新的A时,您取消对旧A的通知订阅并订阅新的A.

答案 1 :(得分:0)

在您的场景中,应该有人负责(MASTER)创建/更新填充坐标的NSMutableArray。在您的情况下,这个主人似乎是控制器A.

然后,您希望另一个对象(控制器B)使用相同的信息(模型)。这个对象是来自控制器A的一种从属:他需要知道何时更改模型以相应地显示它(作为列表)。

我会这样做:控制器A可以设置一个委托,这个委托应该实现一个协议,其中模型的任何更新(由A执行)被通知给委托(B)。协议的粗略定义可以是-(void)modelHasChanged:(NSArray*)theNewModel

控制器B只能对模型进行只读访问:即使控制器A操纵NSMutableArray(更改内容),控制器B也只将其视为不可变的NSArray:这确保只有控制器A才是该模型的真正主控并且B不能改变其内容。

您还可以选择另一种方法:拆分管理模型的对象(NSMutableArray)和表示它的两种方式:作为地图(控制器A),或作为列表(controllerB) 然后你将有3个抽象:

1个操纵NSMutableArray的主人

1个地图的控制器A,您可以在其上设置要显示为NSArray的模型(只读访问前一个主控操作的同一个实例)。

1个控制器B,用于列表,您可以将模型设置为NSArray(只读访问前一个主控操作的同一个实例)。

答案 2 :(得分:0)

只是为答案添加一些内容:

您使用iPhone标记了此问题,因此我认为这是您正在开发的平台。

因此,如果您想使用正确的标准iPhone MVC设计,这听起来像是带有两个UIViewControllers的UINavigationController的完美用例。

让你的AppDelegate实例化一个UINavigationController,并将ViewControllerA设置为root。

为ViewControllerB创建一个新的init / dealloc,其中有一个本地实例var来保存坐标。

- (id)initWithCoords:(NSArray *)coords {
    if (self = [super initWithNibName:@"name of your nib" bundle:[NSBundle mainBundle]]) {
        coordinates = [coords retain];
    }
    return self;
}

- (void)dealloc {
    [coordinates release];
    [super dealloc];
}

现在,只要您想使用选定的coords从ViewControllerA加载ViewControllerB,您只需调用如下方法:

- (void)actionForCoords {
    ViewControllerB *bCon = [[ViewControllerB alloc] initWithCoords:[NSArray arrayWithArray:selectedCoords]];
    [self.navigationController pushViewController:bCon animated:YES];
    [bCon release];
}

这样导航控制器可以在其堆栈上处理大部分工作,提供干净/简单的对象管理和动画。如果你从ViewControllerB回弹到ViewControllerA(例如使用navCon的默认“后退”实现),那么ViewControllerA仍将保留在堆栈上供你使用和修改,同时释放ViewControllerB以释放资源(但准备再次加载新的coords通过“actionForCoords”)。

相关问题