1

预警 - 代码示例有点长...

我有一个可以从我的应用程序中的任何位置访问的单例 NSMutableArray。我希望能够NSMutableArray从多个 NIB 文件中引用,但通过NSArrayController对象绑定到 UI 元素。初始创建不是问题。NSMutableArray当NIB被加载并且一切看起来都很好时,我可以引用单例。

但是,NSMutableArray通过添加或删除对象来更改并不会启动 KVO 来更新NSArrayController实例。我意识到“在控制器背后改变”被认为是 Cocoa-land 的一个不可行的部分,但我没有看到任何其他方式以编程方式更新NSMutableArray并让每个人都NSArrayController得到通知(当然它不起作用。 ..)。

我在下面简化了类来解释。

简化的单例类头:

@interface MyGlobals : NSObject {
    NSMutableArray * globalArray;
}

@property (nonatomic, retain) NSMutableArray * globalArray;

简化单例方法:

static MyGlobals *sharedMyGlobals = nil;

@implementation MyGlobals

@synthesize globalArray;

+(MyGlobals*)sharedDataManager {
    @synchronized(self) {
    if (sharedMyGlobals == nil)
    [[[self alloc] init] autorelease];
}

return sharedMyGlobals;
}

-(id) init {
if(self = [super init]) {
        self.globals = [[NSMutableArray alloc] init];
    }
    return self
}

// ---- allocWithZone, copyWithZone etc clipped from example ----

在这个简化的示例中,数组中对象的标头和模型:

头文件:

@interface MyModel : NSObject {
NSInteger myId;
NSString * myName;
}

@property (readwrite) NSInteger myId;
@property (readwrite, copy) NSString * myName;

-(id)initWithObjectId:(NSInteger)newId objectName:(NSString *)newName;

@end

方法文件:

@implementation MyModel

@synthesize myId;
@synthesize myName;

-(id)init {

[super init];

myName  = @"New Object Name";
myId    = 0;

return self;
}

@end

NSArrayController现在想象两个具有适当实例的NIB 文件。我们会打电话给他们myArrayControllerInNibOnemyArrayControllerInNib2initNIB 控制器中的每个数组控制器设置数组的内容:

// In NIB one init
[myArrayControllerInNibOne setContent: [[MyGlobals sharedMyGlobals].globalArray];

// In NIB two init
[myArrayControllerInNibTwo setContent: [[MyGlobals sharedMyGlobals].globalArray];

当每个 NIBNSArrayController正确初始化绑定到共享数组时,我可以在 UI 中看到您所期望的数组内容。我有一个单独的后台线程,当内容基于外部事件发生更改时,它会更新全局数组。当需要在这个后台线程中添加对象时,我只需将它们添加到数组中,如下所示:

[[[MyGlobals sharedMyGlobals].globalArray] addObject:theNewObject];

这就是事情分崩离析的地方。我不能在全局数组上调用willChangeValueForKeyanddidChangeValueForKey因为共享实例没有键值(我应该在单例类中添加它吗?)

我可以在 NIB 控制器中触发NSNotification并捕获它,然后执行[myArrayControllerInNibOne rearrangeObjects]; 或将内容设置为并将内容nil重新分配给数组 - 但这两者似乎都是黑客和。此外,将 to 设置NSArrayControllernil然后返回到全局数组会导致 UI 内的视觉闪烁,因为内容被清除并重新填充。

我知道我可以直接添加到NSArrayController并且数组得到更新,但我没有看到 a)其他NSArrayController实例将如何更新和 b)我不想将我的后台线程类显式绑定到 NIB 实例(也不我应该这样做)。

我认为正确的方法是在后台线程中以某种方式触发 KVO 通知addObject,或者向存储在全局数组中的对象添加一些东西。但我不知所措。

需要注意的是,我没有使用 Core Data。

任何帮助或帮助将不胜感激。

4

1 回答 1

0

预警 - 回答有点长……</p>

使用为您的域建模的对象。您不需要单例或全局变量,您需要常规类的常规实例。您在全局数组中存储了哪些对象?创建一个代表模型的该部分的类。

如果你使用 NSMutableArray 作为存储,它应该在你的类内部并且对外部对象不可见。例如,如果您正在为动物园建模,请不要这样做

[[[MyGlobals sharedMyGlobals].globalArray] addObject:tomTheZebra];

渡渡鸟

[doc addAnimal:tomTheZebra];

不要试图观察一个可变数组 - 你想观察你的对象的一对多属性。例如。代替

[[[MyGlobals sharedMyGlobals].globalArray] addObserver:_controller]

你要

[doc addObserver:_controller forKeyPath:@"animals" options:0 context:nil];

其中 doc 对于多属性“动物”是 kvo 兼容的。

要使 doc kvo 兼容,您需要实现这些方法(注意 - 您不需要所有这些方法。有些是可选的,但性能更好)

- (NSArray *)animals;
- (NSUInteger)countOfAnimals;
- (id)objectInAnimalsAtIndex:(NSUInteger)i; 
- (id)AnimalsAtIndexes:(NSIndexSet *)ix;
- (void)insertObject:(id)val inAnimalsAtIndex:(NSUInteger)i;
- (void)insertAnimals:atIndexes:(NSIndexSet *)ix;
- (void)removeObjectFromAnimalsAtIndex:(NSUInteger)i;
- (void)removeAnimalsAtIndexes:(NSIndexSet *)ix;
- (void)replaceObjectInAnimalsAtIndex:(NSUInteger)i withObject:(id)val;
- (void)replaceAnimalsAtIndexes:(NSIndexSet *)ix withAnimals:(NSArray *)vals;

好吧,这看起来很吓人,但也没有那么糟糕,就像我说的那样,你并不需要所有这些。见这里。这些方法不需要成为模型接口的一部分,您只需添加:-

- (void)addAnimal:(id)val;
- (void)removeAnimal:(id)val;

并根据 kvc 访问器编写它们。关键是它不是在更改时发送通知的数组,数组只是幕后的存储,它是您的模型类发送对象已添加或删除的通知。

您可能需要重组您的应用程序。你可能需要完全忘记 NSArrayController。

Aaaaaannnnyyywaaayyy……如果你这样做,这一切都不会让你

[[[MyGlobals sharedMyGlobals].globalArray] addObject:theNewObject];

或这个 [doc addAnimal:tomTheZebra];

从后台线程。你不能这样做。NSMutableArray 不是线程安全的。如果它似乎有效,那么最好的情况是 kvo/binding 通知也在后台传递,这意味着您将尝试在后台更新您的 GUI,这是您绝对做不到的。恐怕使数组静态无济于事-您必须为此制定策略..最简单的方法是performSelectorOnMainThread,但除此之外完全是另一个问题。线程很难。

关于那个静态数组 - 停止使用静态,你不需要它。不是因为你有 2 个笔尖、2 个窗口或任何东西。您有一个代表您的模型的实例,并将指向该模型的指针传递给您的 viewControllers、windowControllers 等等。没有单例/静态变量对测试有很大帮助,当然你应该这样做。

于 2010-06-25T00:56:23.857 回答