4

对不起这篇文章的长度;这是为了记录我遇到这个问题的旅程。

我有一个关于 Cocoa 应用程序中需要不时更改的共享对象以及如何最好地存储它以便可以从几个不同的地方访问它的问题。忍受我。

类实现

共享对象被实现为一个类集群(即https://stackoverflow.com/a/2459385/327179),如下所示(注意这Document只是一个类名;它不一定表示我的实际类做):

Document.h

typedef enum {
    DocumentTypeA,
    DocumentTypeB
} DocumentType;

@interface Document : NSObject {}
- (Document *) initWithDocumentType:(NSUInteger)documentType;
- (void) methodA;
- (void) methodB;
@end

Document.m

@interface DocumentA : Document

- (void) methodA;
- (void) methodB;

@end

@interface DocumentB : Document

- (void) methodA;
- (void) methodB;

@end

@implementation Document

- (Document *)initWithDocumentType:(NSUInteger)documentType;
{
    id instance = nil;
    switch (documentType) {
        case DocumentTypeA:
            instance = [[DocumentA alloc] init];
            break;
        case DocumentTypeB:
            instance = [[DocumentB alloc] init];
            break;
        default:
            break;
    }

    return instance;
}

- (void) methodA
{
    return nil;
}

- (void) methodB
{
    return nil;
}

@end

@implementation DocumentA

- (void) methodA
{
    // ...
}

- (void) methodB
{
    // ...
}

@end

@implementation DocumentB

- (void) methodA
{
    // ...
}

- (void) methodB
{
    // ...
}

@end

用户如何与Document

通过菜单项,用户可以随意在 DocumentA 和 DocumentB 之间切换。

发生“切换”时会发生什么

例如,当用户从 切换DocumentA到 时DocumentB,我需要发生两件事:

  1. 我的主要NSViewController( MainViewController) 需要能够使用新对象。
  2. AppDelegate需要更新一个NSTextField恰好位于主窗口内容边框中的内容。(FWIW,我似乎只能为NSTextFieldin分配一个出口AppDelegate

问题

我已经看到很多提到单例作为一种获得全局参考的方法,而不会弄乱一个AppDelegate(主要是这里这里)。也就是说,我没有看到太多关于覆盖这样一个单例的信息(在我们的例子中,当用户切换DocumentADocumentB[或反之亦然] 时,这个全局引用将需要保存新对象)。我不是设计模式方面的专家,但我确实记得听说过单例并不意味着被销毁和重新创建......

所以,鉴于这一切,这是我的问题:

  1. 你将如何存储我的类集群(这样MainViewController并且AppDelegate可以适当地访问它)?
  2. 我是否通过让MainViewController(谁Document大量使用)和AppDelegate(谁管理主窗口[因此,我的NSTextField])都知道Document

如果我对这个问题的思考有误,请随时告诉我;我希望这个实现尽可能正交和正确。

谢谢!


状态更新 #1

感谢@JackyBoy 的建议,这是我采取的路线:

  • Document是在“切换”时“通知”AppDelegateMainViewController通过将新创建的实例传递给它们的那个。
  • 两者都AppDelegate可以根据需要通过 Singleton 实例MainViewController更新对象。Document

这是我的新文件(已简化,以便大家看到问题的症结所在):

Document.h

#import <Foundation/Foundation.h>
@class AppDelegate;
@class MainViewController;

typedef enum {
    DocumentTypeA,
    DocumentTypeB
} DocumentType;

@interface Document : NSObject

@property (weak, nonatomic) MainViewController *mainViewControllerRef;
@property (weak, nonatomic) AppDelegate *appDelegateRef;

+ (Document *)sharedInstance;
- (id)initWithParser:(NSUInteger)parserType;

@end

Document.m

#import "AppDelegate.h"
#import "Document.h"
#import "MainViewController.h"

@interface DocumentA : Document

// ...

@end

@interface DocumentB : Document

// ...

@end

@implementation Document

@synthesize appDelegateRef;
@synthesize mainViewControllerRef;

+ (Document *)sharedInstance
{
    static XParser *globalInstance;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        // By default, I return a DocumentA object (for no particular reason).
        globalInstance = [[self alloc] initWithDocumentType:DocumentA];
    });

    return globalInstance;
}

- (id)initWithDocumentType:(NSUInteger)documentType
{
    Document *instance = nil;
    switch (parserType) {
        case DocumentTypeA:
            instance = [[DocumentA alloc] init];
            break;
        case DocumentTypeB:
            instance = [[DocumentB alloc] init];
            break;
        default:
            break;
    }

    // QUESTION: Is this right? Do I have to store these references
    // every time a new document type is initialized?    
    self.appDelegateRef = (AppDelegate *)[NSApp delegate];
    self.mainViewControllerRef = self.appDelegateRef.mainViewController;

    [self.appDelegateRef parserSwitchedWithParser:instance];
    [self.mainViewControllerRef parserSwitchedWithParser:instance];

    return instance;
}

@end

@implementation Xparser_NSXML

// ...

@end

@implementation DocumentA

// ...

@end

我应该为Document知道 and 的存在而AppDelegate烦恼MainViewController吗?Document此外,当对象更新时,它会重新通知 (即使其中一个启动了更新)AppDelegate,我是否应该对此感到困扰?MainViewController

与往常一样,我感谢大家对此的关注,因为我对理想实施的追求仍在继续。:)


状态更新 #2

@Caleb 的评论帮助我理解,NSNotification对于这个特定问题,基于 - 的设置将不那么笨拙。

谢谢大家!

4

2 回答 2

1

我确实记得听说过单例不应该被销毁和重新创建......

好吧,您可以在其中包含引用,因此您实际上并不是在“破坏”单例,而是他指向的对象。我倾向于在没有应用程序逻辑的情况下离开 App Delegate,所以我通常把它放在其他地方。就您而言,由于您需要从不同的地方访问某些东西,因此拥有一个是有意义的。关于集群,你仍然可以拥有它,你只需让单例访问它并返回适当的对象,如下所示:

Document *myDocument = [[MySingleton defaultManager] createObjectWithType:aType];

你从中获得了一些东西:

  1. 您可以从应用程序中的任何位置访问您的集群

  2. 您将事物解耦,只有一个实体知道您的集群。

  3. 在 Singleton 中,您可以引用 AppDelegate 并与之交互。

  4. 在 Singleton 中,您可以引用正在使用的对象(文档 A、文档 B)

还有一件事,我建议将集群访问方法作为类方法(而不是实例方法)。

于 2013-04-20T09:22:19.190 回答
1

我认为他在这里不需要共享对象,更不用说单例了。您真的需要在任意时间从许多不同的对象中找到当前文档吗?似乎您只有两个对象(应用程序委托和视图控制器)都需要了解当前文档。通知提供了一种简单的管理方法:每当发生切换时,您都可以发布包含新文档的 NSNotification。任何需要了解当前 Document 的对象都将注册“文档切换”通知,并且当通知到达时,它们可以将指向 Document 的指针存储在实例变量或属性中。

于 2013-04-21T07:40:29.413 回答