扩展 UIViewController 以访问应用程序委托是一种反模式吗?

Posted

技术标签:

【中文标题】扩展 UIViewController 以访问应用程序委托是一种反模式吗?【英文标题】:Is extending UIViewController to access the app delegate an antipattern? 【发布时间】:2019-02-05 17:35:41 【问题描述】:

我使用的 API 建议将其客户端保留在应用委托中并通过那里访问它。如果我扩展 UIViewController 以更轻松地访问应用程序委托,这是否是反模式?

extension UIViewController 
    var appDelegate: AppDelegate 
        return UIApplication.shared.delegate as! AppDelegate
    


class SomeViewController: UIViewController 
    ...
    appDelegate.someClient.someMethod()
    ...

通过反模式,我的意思是为了这个简单的方便而扩展整个 UIViewController 类是否过分?总体而言,像这样扩展课程是否有任何负面影响?是否每个视图控制器,即使它不访问 API,现在都隐式指向应用委托?

【问题讨论】:

“一种建议将其客户端保留在应用程序委托中的 API” 听起来好像一种不好的做法已经被强加给你了...... @JoshCaswell 我不明白为什么。这是在应用程序的整个生命周期中持久保留任何需要可靠持久的东西的正确方法。 CLLocationManager 和 CMMotionManager 是常见的用例。 【参考方案1】:

如果我扩展 UIViewController 以更轻松地访问应用程序 代表,这算不算反模式?

extension UIViewController 
    var appDelegate: AppDelegate 
        return UIApplication.shared.delegate as! AppDelegate
     

没有;恰恰相反。需要访问应用程序委托并将其转换为它的实际类以便能够访问应用程序委托中的属性或调用方法是很常见的。如果这可能需要在多个地方完成,计算属性是提供速记的标准符号。 (不需要为此使用扩展名,但这样做并没有什么坏处,而且可能有符号优势;例如,扩展名可以位于应用委托文件中。)


在评论中,您会思考扩展 UIViewController 的智慧。我想您有两个视图控制器需要访问应用程序委托,而其他许多则不需要。

现在,首先,这并没有真正改变我的答案。我看不出它会造成什么危害,或者它是如何以任何方式让所有视图控制器能够通过快捷方式访问应用程序委托的反模式,即使它们中的许多人实际上从未这样做过。所有的视图控制器都已经能够很多做他们中的大多数人实际上永远不会做的事情。

但是假设你有不同的感觉。然后你可以在每个相关的视图控制器中显式地实现appDelegate(以违反 DRY 为代价)。

或者(这是很酷的部分)你可以声明一个协议和一个协议扩展,并且只有相关的视图控制器采用它。所以,这是你的 AppDelegate.swift 文件:

import UIKit

protocol AppDelegateAccesser 
extension AppDelegateAccesser 
    var appDelegate: AppDelegate 
        return UIApplication.shared.delegate as! AppDelegate
    


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate 
    // ....

现在假设只有 MyViewController 类实际上需要访问应用程序委托。那你就说

extension MyViewController : AppDelegateAccesser 

这会将appDelegate 注入 MyViewController 但没有其他类。因此,您只需为需要访问应用程序委托的每个视图控制器执行此操作,而不是其他。与您最初提出的解决方案相反,我认为这是一个完全不必要的练习,但它确实解决了仅将代码注入某些类的问题。


注意:在 Swift 5 中,声明只有 UIViewController 子类可以采用此协议是合法的。你可能会喜欢。

【讨论】:

举个例子,你正在做的将是在应用程序委托中存储和访问单个 CLLocationManager 的正确方法,这在 ios 应用程序中是非常常见的需求。 并且通过扩展整个UIViewController 类,它不会造成每个视图控制器(即使它不访问 API)隐式指向应用程序委托的情况? 它确实会造成这种情况,但这有什么害处?但是,如果您有多个但不是所有必须执行此访问的视图控制器,则只需使用协议和协议扩展,并让相关的视图控制器采用该协议。我会将其添加到我的答案中。【参考方案2】:

将此函数放入 AppDelegate 文件,以便您可以在整个项目中访问 AppDelegate。

class func shared() -> AppDelegate 
     return UIApplication.shared.delegate as! AppDelegate

你可以像这样使用:

AppDelegate.shared()

【讨论】:

我赞成。不是因为带有控制器扩展的解决方案本身是错误的,而是这只是一个更好的解决方案。

以上是关于扩展 UIViewController 以访问应用程序委托是一种反模式吗?的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式向 UIView 或 UIViewController 添加约束?

iOS Dev - 如何检测用户通过按“返回”访问此 UIViewController

UIViewController 容器视图内容扩展容器边界

如何在 iOS 中以编程方式从基本 UIViewController 向我的所有视图控制器添加视图

在 UIViewController Storyboard 中访问 UIView

访问当前活动显示的 UIViewController