代码之家  ›  专栏  ›  技术社区  ›  Goles

访问两个uiviewcontrollers之间的nsmutablearray的正确方法?[关闭]

  •  2
  • Goles  · 技术社区  · 16 年前

    设计问题:

    我有一个viewcontroller a,它包含一个nsmutablearray*。view controller a负责向用户显示映射,当用户与该映射交互时,viewcontroller a用坐标对象填充nsmutablearray*。

    nsmutablearray*中包含的信息稍后应显示在uitable中,uitable包含在与viewcontroller b关联的另一个视图中。

    viewcontroller b访问由a填充的nsmutablearray的最正确方法是什么?(保留对nsmutablearray*的引用)。

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

    3 回复  |  直到 16 年前
        1
  •  2
  •   zoul    16 年前

    最正确的方法是什么 视图控制器B访问 由填充的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一个指向A的指针的最佳位置(只需创建一个 IBOutlet 对于A在B)

    使用单独模型类的第二个解决方案是一个“更干净”的MVC,并且不需要控制器相互了解——它们都依赖于模型类。您也可以实例化模型并将其链接到接口生成器中的控制器。

    顺便说一句:如果b想监视a的某些属性的变化,那么它必须在设置到a的链接之后订阅。一个简单但有点错误的方法是订阅 viewDidLoad 方法B。这很方便,但是如果到A的链接在此之后发生更改,则通知不会相应更改。更难但正确的订阅方式是在setter中订阅a当有人设置新a时,您取消对旧a的通知订阅并订阅新a。

        2
  •  0
  •   yonel    16 年前

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

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

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

    控制器B只有对模型的只读访问权:即使控制器A操纵一个nsmutableArray(以更改内容),控制器B也只将其视为一个不可变的nsArray:这确保了只有控制器A是该模型的真正主控制器,而B不能更改其内容。

    您还可以选择另一种方法:分割管理模型的对象(nsmutablearray)和表示它的两种方法:作为映射(controller a)或作为列表(controllerb) 然后您将得到3个摘要:

    1个操纵nsmutable数组的主控

    1个控制器A,用于可以将模型设置为显示为nsarray的映射(对上一个主控操作的同一实例的只读访问)。

    1控制器b,用于可以将模型设置为显示为nsarray的列表(对上一个主控操作的同一实例的只读访问)。

        3
  •  0
  •   Matthew McGoogan    16 年前

    只是为了给答案加上一点变化:

    你用iphone标记了这个问题,所以我假设这是你正在开发的平台。

    因此,如果您希望使用正确和标准的iphone mvc设计,这听起来像一个带有两个uiviewcontroller的uinavigationcontroller的完美用例。

    让appdelegate实例化一个uinavigationcontroller,并将viewcontrollera设置为根。

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

    - (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];
    }
    

    现在,每当您想从viewcontrollera加载viewcontrollerb时,只要调用如下方法:

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

    这样导航控制器就可以为您处理其堆栈上的大部分工作,提供干净/简单的对象管理和动画。如果您从viewcontrollerb(例如navcon的默认“back”实现)弹出回到viewcontrollera,那么viewcontrollera仍将保留在堆栈上供您使用和修改,而viewcontrollerb将被释放以释放资源(但是准备用新的coords vIA“ActionForCoords”)。