Swift - 仅当类符合使用方法调配的协议时才扩展类

Ada*_*ter 3 uiviewcontroller ios swift swift-extensions method-swizzling

我有一个名为 的协议NakedNavigationBar

\n\n

我还有一个扩展,可以扩展所有UIViewController符合NakedNavigationBar.

\n\n

问题是,在扩展中我想添加默认行为,以便在初始化时UIViewController,我们在UIViewController.

\n\n

这是我的协议和扩展:

\n\n
import UIKit\n\n\nprotocol NakedNavigationBar\n{\n\n}\n\nextension NakedNavigationBar where Self: UIViewController\n{\n    public override class func initialize()\n    {\n        struct Static\n        {\n            static var token: dispatch_once_t = 0\n        }\n\n        dispatch_once(&Static.token)\n        {\n            let originalSelector = #selector(viewWillAppear(_:))\n            let swizzledSelector = #selector(nakedNavigationBar_viewWillAppear(_:))\n\n            let originalMethod = class_getInstanceMethod(self, originalSelector)\n            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)\n\n            let didAddMethod = class_addMethod(self,\n                                               originalSelector,\n                                               method_getImplementation(swizzledMethod),\n                                               method_getTypeEncoding(swizzledMethod))\n\n            if didAddMethod\n            {\n                class_replaceMethod(self,\n                                    swizzledSelector,\n                                    method_getImplementation(originalMethod),\n                                    method_getTypeEncoding(originalMethod))\n            }\n            else\n            {\n                method_exchangeImplementations(originalMethod, swizzledMethod)\n            }\n        }\n    }\n\n\n    //  MARK: - Swizzling Methods\n\n    func nakedNavigationBar_viewWillAppear(animated: Bool)\n    {\n        self.nakedNavigationBar_viewWillAppear(animated)\n\n        print("VWA Swizzling...")\n\n        //  Hide the navigation bar\n        _setNavigationBarVisible(isVisible: false)\n    }\n\n\n    //  MARK: -\n\n    private func _setNavigationBarVisible(isVisible isVisible: Bool)\n    {\n        // (Changes background and shadow image)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我在构建时遇到的错误是:\n错误

\n\n

本质上告诉我,我无法扩展协议,因为它没有\xe2\x80\x99t 的方法UIViewController。但据我了解,where Self: UIViewController应该使其仅适用于UIViewController,仅当它符合 时才扩展视图控制器NakedNavigationBar

\n\n

最初的扩展是extension UIViewController: NakedNavigationBar,但这使得我的所有UIViewControllers 立即符合NakedNavigationBar而不仅仅是我选择的。

\n

Rob*_*ris 5

据我了解,从 swift 3.0 开始,(从语义上讲)您理想想要做的事情如下:

protocol NakedNavigationBar
{
    //your protocol definition
}

extension UIViewController where Self: NakedNavigationBar
{
    //method swizzling and all the goodies
}
Run Code Online (Sandbox Code Playgroud)

请注意,我更改了扩展声明的顺序,因为您想要的不是协议的默认实现,而是UIViewController当特定子类符合NakedNavigationBar. 问题是,目前该语法无法编译!!,我们无法Self向类扩展添加要求=((我感受到你的痛苦)。

另一方面,您实际尝试做的事情(具有Self要求的协议扩展)编译得很好,但问题是您无法覆盖协议扩展中的类方法,并且无法尝试混淆您:

public override class func initialize()
Run Code Online (Sandbox Code Playgroud)

当向协议扩展添加需求时,您可以做的Self是调用该类(在本例中UIViewController)提供的任何公共 API,因此您可以执行以下操作:

protocol NakedNavigationBar
{
    func a() {
        //define cool stuff...
    }
}

extension NakedNavigationBar where Self: UIViewController
{

    func b() {
        //invoke stuff from NakedNavigationBar
        a()
        //or invoke stuff from UIViewController
        let viewLoaded = self.isViewLoaded
    }
    //but what we CAN'T do are class overrides
    override class func initialize() {} //---->compilation error 
}
Run Code Online (Sandbox Code Playgroud)

我希望你觉得这个解释有用,很抱歉给你带来坏消息,但目前你想要实现的目标对我来说似乎并不能以 Swifty 类型安全的方式实现。

但不用担心!生活还要继续!当我遇到与你相同的用例并遇到同样的困难时,我设法让事情正常工作,但它有一个缺点:这不是 Swifty 类型安全的做事方式(也许我们可以改进如果 Swift 的后续版本允许对类扩展的自我要求,则会出现这种情况#fingersCrossed)

简而言之,解决方案是这样的(以您的原始代码为例):

import UIKit

protocol NakedNavigationBar
{

}

extension UIViewController //------->simple extension on UIViewController directly
{
    public override class func initialize()
    {
        //swizzling stuff switching viewWillAppear(_: Bool) with nakedNavigationBar_viewWillAppear(animated: Bool)
    }

    //  MARK: - Swizzling Methods

    func nakedNavigationBar_viewWillAppear(animated: Bool)
    {
        self.nakedNavigationBar_viewWillAppear(animated)

        print("VWA Swizzling...")

        //------->only run when self conforms to NakedNavigationBar
        if let protocolConformingSelf = self as? NakedNavigationBar { 
            print("UIViewControllers that conform to NakedNavigationBar, yay!!")
            //here it's safe to call UIViewController methods OR NakedNavigationBar on protocolConformingSelf instance
        }

    }


    //  anything else you want for UIViewController
}
Run Code Online (Sandbox Code Playgroud)

缺点:

  • 不是做事的“快速方式”
  • swizzling 方法将被调用并在 ALL 上生效UIViewControllers,即使我们只验证那些符合NakedNavigationBar协议来运行敏感代码行的方法。

我非常希望它有帮助。如果有人找到另一种(更好的)方法,很高兴听到它!