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

Is extending UIViewController to access the app delegate an antipattern?

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

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

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

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

If I extended UIViewController to make it easier to access the app delegate, is this any way an antipattern?

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

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


在评论中,您思考扩展 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 class 实际上需要访问应用程序委托。那你就说

extension MyViewController : AppDelegateAccesser {}

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


注意:在 Swift5 中声明只有 UIViewController subclass 可以采用此协议是合法的。你可能会喜欢。

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

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

你可以像这样使用:

AppDelegate.shared()