抱歉这篇文章有点长,但是它旨在记录我解决这个问题的过程。
我有一个关于Cocoa应用程序中共享对象的问题,需要不时更改,并且如何最好地存储它,以便从几个不同的位置访问。请耐心等待。
类实现
共享对象被实现为一个类簇(即 https://dev59.com/aXI-5IYBdhLWcg3wc4Cw#2459385),看起来像下面这样(请注意,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
用户如何与文档
交互
通过菜单项,用户可以随意在DocumentA和DocumentB之间切换。
“切换”发生时会发生什么
当用户从DocumentA
切换到DocumentB
时,我需要发生两件事:
- 我的主要
NSViewController
(MainViewController
)需要能够使用新对象。 - 我的
AppDelegate
需要更新位于主窗口内容边框中的NSTextField
。(值得一提的是,我只能在AppDelegate
中为NSTextField
分配一个outlet)
问题
我看到单例被提及得相当频繁,作为一种在不混乱AppDelegate
的情况下拥有全局引用的方法(主要参考这里和这里)。话虽如此,我没有看到关于覆盖这样一个单例的太多信息(在我们的情况下,当用户从DocumentA
切换到DocumentB
[或反之亦然]时,这个全局引用需要保存新对象)。我不是设计模式的专家,但我确实记得听说单例不应该被销毁和重新创建...所以,鉴于所有这些,这里是我的问题:
- 你将如何存储我的类簇(以便
MainViewController
和AppDelegate
可以适当地访问它)? - 如果我让
MainViewController
(大量使用Document
)和AppDelegate
(管理主窗口[因此,我的NSTextField
])都了解Document
,是否会混淆问题?
如果我对这个问题的思考方式不正确,请随时告诉我;我希望这个实现尽可能正交和正确。
谢谢!
状态更新 #1
感谢 @JackyBoy 的建议,这是我采取的路线:
Document
是那个在“切换”时通过传递新创建的实例来“通知”AppDelegate
和MainViewController
的。- 无论何时,
AppDelegate
和MainViewController
都可以通过 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
知道 AppDelegate
和 MainViewController
的存在吗?此外,当 Document
对象更新时,是否应该担心它重新通知两个 AppDelegate
和 MainViewController
(即使其中一个是发起更新的)?一如既往,感谢大家对我的支持,帮助我寻找最佳实现方案。 :)
更新 #2
来自@Caleb的评论帮助我理解,在这个特定的问题中,基于NSNotification
的设置会更加灵活。
谢谢大家!
AppDelegate
和MainViewController
都需要初始化集群的新版本。 当然,我可以控制它,但是如果需要通知DocumentA
和DocumentB
之间的更改,则两个类都需要被通知,这似乎有点笨拙。 我想AppDelegate
可以将其DocumentA
/DocumentB
实例传递给MainViewController
? - ABach[self.appDelegateRef parserSwitchedWithParser:instance]; [self.mainViewControllerRef parserSwitchedWithParser:instance];
- Rui Peres