2010-02-14 33 views
2

設計問題:訪問兩個UIViewControllers之間的NSMutableArray的正確方法?

我有ViewController A,它包含一個NSMutableArray *。 ViewController A負責向用戶顯示Map,當用戶與該地圖交互時,視圖控制器A用座標對象填充NSMutableArray *。

中包含的的NSMutableArray *信息,應在稍後顯示其中包含在asociated到的ViewController B.

什麼是視圖控制器B中的最正確的方式來訪問所填充NSMutableArray的另一種觀點認爲一個UITable由A? (A持有對NSMutableArray *的引用)。

應該有幾種方法來做到這一點,但爲了保持自己純粹的MVC,我真的很想知道你的意見。

回答

2

什麼是 視圖控制器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.(只需在B中爲A創建一個IBOutlet

第二種解決方案與單獨的模型類是「更乾淨」的MVC,不需要控制器相互瞭解 - 它們都取決於模型類。您也可以實例化模型並將其鏈接到Interface Builder中的控制器。順便說一句:如果B想要觀察A的某些屬性的變化,則必須在已經設置了A的鏈接之後訂閱。一個簡單但很輕微的錯誤方法是在B的viewDidLoad方法中訂閱。它很方便,但如果在此之後到A的鏈接被更改,通知不會相應地改變。訂閱的更難但正確的方式是在A的設置者 - 當有人設置新的A時,您取消通知訂閱舊A並訂閱新A。

+0

@zoul:您的解決方案非常有趣,我一直想使用KVO。只是一個簡單的問題:您的解決方案涉及@interface B包含對@interface A的引用?(我在創建應用程序時將B鏈接到A的一個指針)。如果是這樣,那麼分配這個指針的最好方法是什麼?我使用Interface Builder實例化A和B. – Goles 2010-02-15 10:08:45

+0

我已經更新了答案。如果不夠清楚,請不要猶豫,再次提問。 – zoul 2010-02-15 10:30:33

+0

你的答案是我在這個網頁上獲得的最好的答案之一。非常感謝你。順便說一句,有一個小的錯字,你可以在這一行修復:NSAssert([keyPath isEqualToString:@「foos」],@「Somethind fishy going on。」); //這是固定行(缺少']')。再次感謝! – Goles 2010-02-15 10:44:02

0

在你的場景中,應該有人負責(MASTER)創建/更新座標填充的NSMutableArray。在你的情況下,這個主人似乎是控制器A.

然後,你想要另一個對象,控制器B,使用相同的信息(模型)。該對象是控制器A的一種從屬:他需要知道模型何時更改以相應地顯示(作爲列表)。

我會這樣做的方式:控制器A可以設置一個委託,並且這個委託應該實現一個協議,其中模型的任何更新(由A執行)都會被通知給委託(B)。協議的粗略定義可能是:控制器B只能對模型進行只讀訪問:即使控制器A操作NSMutableArray(更改內容),控制器B也只能將其視爲不可變的NSArray:它可以確保只有控制器A是這個模型的真正主人並且B不能改變其內容。你可以選擇另一種方法:分割管理模型的對象(NSMutableArray)和2種表示方式:作爲一個映射(控制器A)或者作爲一個列表(控制器B) 然後你可以有3個抽象:

1主操縱要在其上可以設置爲被顯示爲一個的NSArray模型(只讀訪問由先前的主操縱相同的實例)的地圖上的NSMutableArray

1控制器A。

1控制器B用於您可以將模型設置爲顯示爲NSArray的列表(只讀訪問由前一個主控操作的同一個實例)。

+0

@yonel你好!我沒有完全解釋你設置一個委託和實施一個協議......你可以深入一點,或者編輯你的文章,並使你的解釋更清楚?再次感謝! – Goles 2010-02-14 23:54:45

+0

難道你不能簡單地使用KVO而不是協議?至少如果性能考慮因素不強迫你進行部分更新。 – zoul 2010-02-15 06:04:36

+0

@zoul:我不習慣KVO方法,所以我不能說:)這就是爲什麼我要以強烈的興趣閱讀你的答案! – yonel 2010-02-15 07:25:44

0

只是拋出一個小品種到答案:

您標記與iPhone這個問題,所以我認爲是你正在開發的平臺。

因此,如果您想使用正確和標準的iPhone MVC設計,這聽起來像是具有兩個UIViewControllers的UINavigationController的完美用例。

讓您的AppDelegate實例化一個UINavigationController,並將ViewControllerA設置爲根。

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

現在,每當你想從ViewControllerA加載ViewControllerB與所選COORDS您只需調用一個方法如如下:

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

這樣,導航控制器處理大部分工作的你在它的堆棧上,提供清潔/簡單的對象管理和動畫。如果你從ViewControllerB回滾到ViewControllerA(例如使用navCon的默認「後退」實現),那麼ViewControllerA仍然會保留在堆棧上供你使用和修改,而ViewControllerB被釋放以釋放資源(但準備好再次加載通過「actionForCoords」新的協調)。

相關問題