为啥不强制在 NIB 中使用严格的单例应用程序委托对象?

Posted

技术标签:

【中文标题】为啥不强制在 NIB 中使用严格的单例应用程序委托对象?【英文标题】:Why not enforce strict singleton application delegate object to use in NIBs?为什么不强制在 NIB 中使用严格的单例应用程序委托对象? 【发布时间】:2014-04-04 19:57:12 【问题描述】:

我只是绕着圈子跑,所有这些都归结为在不是 NSMainNibFile 的辅助 NIB 中实例化了一个应用程序委托对象。令人惊讶的是,拥有两个应用程序代表意味着您拥有单独的 managedObjectContexts

这是一个想法——我可以让我的应用程序委托类成为单例吗?并在更多的 XIB 中安全地实例化它?那会破坏什么?

此外,*** 上也有人提到 [[UIApplication sharedApplication] delegate] 是“单例”,但似乎 UIApplicationDelegate 协议并不能保证这一点,超类 UIResponder 也不是单例。那么我在 ios 上也能在这方面自取其辱吗?

[编辑] 看起来你可以在 iOS 中将 UIApplicationMain 中的 delegateClassName 设为 nil 并让主 NIB 加载委托对象,因此如果使用主 NIB,你可以创建在 OSX 上看到的 App Delegate 对象模式。

[edit2] MainMenu.xib 对于新的非文档应用程序的外观截图。项目使用此对象创建,应用程序委托类使用窗口属性创建。问题是在其他 NIB 中获得那个方便的对象,并且该对象与 [NSApp delegate] 相同

【问题讨论】:

这里只有一个应用程序委托,即在应用程序启动时设置的委托。您可以创建应用委托类的第二个对象,但它不是应用委托,无论名称如何。 (请注意,您可以对单例类做同样的事情——在“工厂”之外创建多个实例。) (您不应该在 XIB 中创建应用程序委托。我什至不确定您如何做到这一点。) @Hot Licks - 只需创建一个将自定义类设置为您的应用委托类的对象。顺便说一句,当主 nib 文件加载到 NSApplicationMain 中的某个位置时,应用程序的委托被设置,这就是主 NIB 不使用它的原因。需要明确的是——这样做很糟糕,因为委托类不保证该类的单个实例。 好的,我说的是 iOS,按照惯例,应用程序代理加载在 main 中。 【参考方案1】:

只需在您现有的 App Delegate 中执行此操作(只有一个!)

// In the header file    
+ (AppDelegate*) sharedInstance;


// In the body
+ (AppDelegate*) sharedInstance 

    return (AppDelegate*) [[UIApplication sharedApplication] delegate];


然后在任何你想引用你的 App Delegate 的地方,你可以简单地使用 [AppDelegate sharedInstance] 后跟你想调用的属性或实例方法。

【讨论】:

不能在 XIB 中工作——你只能指定类类型。不是类方法。如果你可以调用任何方法,我会调用[NSApp delegate]——但据我所知,XIB 只是分配初始化自定义类。 是的,完全误解了这个问题......编辑有助于澄清你所追求的。对不起!【参考方案2】:

无论如何,您都不应该使用应用程序委托来处理与核心数据有关的事情。所以让它成为一个强制的单例是没有意义的。

理想情况下,根本不需要引用它。

【讨论】:

我倾向于同意,但是在非文档应用程序中,模板建议应用程序委托拥有 managedObjectContext 堆栈,这也是 xcode 创建此应用程序委托对象的情况。假设你有一个像 GIMP 这样有十亿个窗口的应用程序,但它不是基于文档的——你的共享 MOC 指针在哪里? 对于投反对票的人...hollance.com/2012/02/dont-abuse-the-app-delegate 今天偶然发现,“在 OS X 中:在单协调器应用程序中,您可以直接从应用程序委托获取应用程序的上下文。” -- Apple's "Accessing the Core Data Stack"【参考方案3】:

好的,在这个问题被投赞成票之后,由于不知道为什么,我继续调查我自己的答案。我认为让您的应用程序委托类成为真正的单例很有用,这样您就不会对 NIB 感到头疼。 我不明白它为什么有害。而且我认为,如果您的应用程序只有一个用户界面,那么让应用程序委托拥有所有 NIB 的核心数据堆栈并不是不合理的。但是,推荐的设计模式是让每个窗口或视图控制器都被传递 ManagedObjectContext 指针,并通过文件的所有者占位符而不是使用 App Delegate 对象来访问 MOC。

但另一方面,“共享用户默认控制器”单例的情况有所不同,它在每个 NIB 中都有一个特殊的对象。我们不必向每个控制器传递一个指向它的指针,以便每个视图都可以访问它。它只是无所不在。应用程序委托是不同的。每个 NIB 中都没有“Shared App Delegate”对象。是的,有理由从不与 NIB 中的应用代理交谈,但这不是问题的答案。

所以,一个答案。

单例设计模式: Apple 很久以前在this deprecated reference document-- Creating a Singleton Instance. 中报道过

原来我希望我的应用程序委托类实现的是“严格”实现,而不是拥有可以创建应用程序委托类的其他对象的工厂方法。这里的一个不同特性是让[NSApp delegate] 成为主指针,而不是应用程序委托类函数。

严格的实现必须为我的应用程序委托类覆盖 allocWithZone(因为 alloc 调用 allocWithZone)。

+ (MYAppDelegate*)allocWithZone:(NSZone *)zone

    if ([NSApp delegate] == nil) return [super allocWithZone:zone];
    return [NSApp delegate];


- (MYAppDelegate*)copyWithZone:(NSZone *)zone

    return self;

初始化只返回[super init] 就可以了,所以它不需要覆盖。

似乎有效。如果没有,我会更新。

[更新] 我也一直在使用NSBundleloadNibNamed:owner:topLevelObjects: 调查NIB 加载——但似乎我会用新的应用程序委托对象返回一个数组,即使是从那个方法。该方法允许获取指向 NIB 中***对象的指针,而无需为它们创建其他出口。除了 MainMenu 之外,在 XIB 中获取应用程序委托对象的最佳方法似乎仍然是使用类似上面的代码。

[另一个更新] 为什么它可能有害: 根据this document 中的“OS X 中的***对象可能需要特殊处理”部分,我有充分的理由相信即使使用 ARC,我的这个答案也会增加 [NSApp 委托] 上的保留计数,但是如果我觉得可以为具有***的窗口/视图控制器的 dealloc 中的应用程序委托做一个桥接和发布,那就见鬼了应用程序委托的对象。另外,这意味着应用程序委托类之外的代码。

【讨论】:

让 NIB 创建应用程序委托,而不是 main 函数或其他任何重要的函数,这似乎仍然很奇怪。 NSApplicationMainUIApplicationMain 负责挂钩单例应用程序委托。在 OSX 中,NSMainNibFile,通常是 MainMenu.xib,有一个作为 NSApplicationMain 一部分的应用委托对象。 UIApplicationMain 有这个选项。这段代码的作用是允许其他 NIB 中的应用程序委托对象,这将调用 init.可能是保留问题,但因为它是应用程序委托...... 是的。另一个答案可能是您应该子类化窗口或视图控制器,并确保它的 managedObjectContext 属性设置为应用程序委托创建的 managedObjectContext。然后创建与文件所有者的 managedObjectContext 的绑定。这就是您的链接所暗示的。像我上面的答案这样的替代方案,或者这个:gentlebytes.com/blog/2009/09/02/coredata-bindings-multiple-nibs 非常难看。 或者以 IB 为中心的替代方案:***.com/questions/20180939/… ... 将 MOC 对象拖到您的 NIB 中并将其连接到 awakeFromNib 中的代理 PSC,尽管您随后有一个单独的 MOC,它将重新创建让我来到这里的问题。

以上是关于为啥不强制在 NIB 中使用严格的单例应用程序委托对象?的主要内容,如果未能解决你的问题,请参考以下文章

NSManagedObjectContext 的单例?死板的?依赖注入?

为啥苹果推荐使用 dispatch_once 来实现 ARC 下的单例模式?

SwiftObjective-C 单例模式 (Singleton)

为啥单例对象创建的scala程序不需要静态main方法?

swift 3中的单例计算属性

Unity 单例模式