【问题标题】:Class Cluster as a Singleton?将集群类作为单例?
【发布时间】:2013-04-22 00:39:11
【问题描述】:

对这篇文章的长度感到抱歉;这是为了记录我遇到这个问题的过程。

我有一个关于 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,我似乎只能在AppDelegate 中为NSTextField 分配一个出口)

问题

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

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

  1. 您将如何存储我的类集群(以便MainViewControllerAppDelegate 可以适当地访问它)?
  2. 我是否通过让MainViewController(大量使用Document)和AppDelegate(管理主窗口[因此,我的NSTextField])都知道Document来混合关注?李>

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

谢谢!


状态更新 #1

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

  • Document 是在“切换”时通过传递新创建的实例来“通知”AppDelegateMainViewController
  • AppDelegateMainViewController 都可以根据需要通过 Singleton 实例更新 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 知道AppDelegateMainViewController 的存在而烦恼吗?此外,当Document 对象更新时,它会重新通知AppDelegateMainViewController(即使其中一个启动了更新),我是否应该对此感到困扰? p>

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


状态更新 #2

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

谢谢大家!

【问题讨论】:

  • 一个很好的问题。 +1。

标签: objective-c cocoa singleton


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我确实记得听说过单身人士并不是要被销毁的 并重新创建...

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

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

    你从中得到一些东西:

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

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

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

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

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

    【讨论】:

    • 感谢您的意见。我仍然遇到的一件事是:似乎使用此设置,当用户从DocumentA 切换到DocumentBAppDelegateMainViewController 需要both 初始化一个新的Cluster 的版本。当然,我可以控制它,但是这两个类都需要被通知从 DocumentADocumentB 的更改似乎有点笨拙(反之亦然)。我想 AppDelegate 可以将其 DocumentA/DocumentB 实例传递给 MainViewController?
    • 知道了。非常感谢。如果您不介意,我将试一试,并就我的问题提供状态更新;非常感谢您关注我是否以正确的方式思考问题。
    • 两件事 ABAch: 1) 你应该只在 - (id)initWithDocumentType:(NSUInteger)documentTypelike - (id)initWithDocumentType:(NSUInteger)documentType withMainViewController:(MainViewController*)mainViewController withAppDelegate:(AppDelegate*)appDelegate. After that you could use a method like: - (void)switchDocumentWith:(Document *)aDocument` 和那里进行必要的初始化:` [self.appDelegateRef parserSwitchedWithParser:instance]; [self.mainViewControllerRef parserSwitchedWithParser:instance];`
    • 知道了。感谢您的持续投入。目前,@Caleb 的回答在我的应用程序中更有意义。也就是说,我非常感谢您的所有帮助。
    • @Caleb 方法也是一个完全有效的选择。
    猜你喜欢
    • 1970-01-01
    • 2016-11-11
    • 2013-01-17
    • 1970-01-01
    • 2014-02-07
    • 2013-06-01
    • 1970-01-01
    • 2015-03-13
    • 2010-11-14
    相关资源
    最近更新 更多