【问题标题】:Swift proper use of protocol extensionSwift 正确使用协议扩展
【发布时间】:2019-06-28 02:40:37
【问题描述】:

我正在尝试提取一些代码库以供重复使用。我的方法是使用ProtocolProtocol Extension 而不是一般的BaseClass

我在下面创建了 protocolprotocol extension

protocol MovieDisplay {

    var collectionView: UICollectionView! { get set }
    var refreshControl: UIRefreshControl! { get set }

}

extension MovieDisplay where Self: UIViewController {

    var refreshControl: UIRefreshControl {
        let rc = UIRefreshControl()
        rc.backgroundColor = .clear
        rc.tintColor = .lightGray
        if #available(iOS 10.0, *) {
            collectionView.refreshControl = rc
        } else {
            // Fallback on earlier versions
            collectionView.addSubview(rc)
        }
        return rc
    }

}

在采用我这样声明的协议的主类中(使用refreshcontrol 的默认实现)

class PopularMovieVC: UIViewController, MovieDisplay {

    @IBOutlet weak var collectionView: UICollectionView!

}

问题是涉及refreshcontrol 的功能不起作用。只有当我在主类中显式声明 refreshcontrol 变量并将扩展转换为函数并在主类中调用它时,它才有效,如下所示:

func setupRefreshControl() {
            refreshControl.backgroundColor = .clear
            refreshControl.tintColor = .lightGray
            if #available(iOS 10.0, *) {
                collectionView.refreshControl = refreshControl
            } else {
                // Fallback on earlier versions
                collectionView.addSubview(refreshControl)
            }
}

如何正确配置protocolprotocol extension 以实现默认实现?

【问题讨论】:

    标签: swift protocols protocol-extension


    【解决方案1】:

    它不起作用,因为计算属性没有被隐式调用。

    viewDidLoad 中添加这一行应该会初始化刷新控件

    _ = refreshControl
    

    在这种情况下,我真的更喜欢基类

    【讨论】:

    • 添加 _ = refreshControl 也不起作用。你知道任何不涉及基类的方法吗?
    • 如果它不起作用,你将与框架作斗争。基类有什么问题?
    • 基类没有错。我只是不想在以后的状态下得到臃肿的基类,并想尝试制作一些可以单独重用的协议和协议扩展
    【解决方案2】:

    您的协议需要一个可获取和可设置的refreshControl(返回UIRefreshControl!),但您的默认实现只提供了一个getter(并且该getter 返回一个不同的类型,UIRefreshControl)。您的默认实现还会在每次访问时返回不同的UIRefreshControl,并在每次访问时修改collectionView。我认为这些都不是你的意思。

    正如 vadian 所说,如果您想自动修改 collectionView.refreshControl,我认为您真正想要的是基类。遵守协议绝不应该导致对其他属性的隐式更改,而且在大多数情况下它不会。想象一下,如果PopularMovieVC 在另一个模块的扩展中符合MovieDisplay。这充其量只会导致混乱。

    协议一致性扩展了类型的使用方式,例如添加新方法。它不会改变类型本身的任何内容。如果您想更改类型本身的某些内容,则需要继承或组合之类的内容,而不是协议一致性。


    如果您这样做,我不会以这种方式使用协议。我只是创建这样的扩展:

    extension UIRefreshControl {
        static func makeStandard(attachedTo collectionView: UICollectionView) -> UIRefreshControl {
            let rc = UIRefreshControl()
            rc.backgroundColor = .clear
            rc.tintColor = .lightGray
            if #available(iOS 10.0, *) {
                collectionView.refreshControl = rc
            } else {
                // Fallback on earlier versions
                collectionView.addSubview(rc)
            }
            return rc
        }
    }
    
    extension UIActivityIndicatorView {
        static func makeStandard() -> UIActivityIndicatorView {
            return UIActivityIndicatorView(style: .gray)
        }
    }
    

    那么你的视图控制器可能看起来像:

    class MyViewController: UIViewController {
        private var refreshController: UIRefreshControl!
        @IBOutlet var collectionView: UICollectionView!
        let activityIndicator = UIActivityIndicatorView.makeStandard()
    
        override func viewDidLoad() {
            refreshController = .makeStandard(attachedTo: collectionView)
        }
    }
    

    不需要协议,这允许您在同一个视图控制器中处理多个集合视图,或任何其他不寻常的情况。它也更清楚地表明调用此方法将修改集合视图。

    【讨论】:

    • 您好,感谢您的澄清。那么推荐的方法是什么? (如果没有基类)。应该使用我的波纹管解决方案,它在协议扩展中转换为默认函数并在主类中调用它?
    • 没错。您可以在MovieDisplay 上添加setupRefreshControl 作为扩展名,并在viewDidLoad 或其他方便的位置调用它。所以它变成了一种方便的方法,而不是试图修改类型的行为。
    • 感谢 UIActivityIndi​​catorView 不涉及对其他属性的隐式更改。方法还是一样? (在协议中声明变量,在协议扩展中提供默认设置,在主类中声明变量,调用setup方法?)
    • 参见上面的 cmets。我会放弃协议。它们不是必需的。如果您想为您的应用程序提供某种标准值,只需使用静态函数即可。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-04
    • 1970-01-01
    • 1970-01-01
    • 2018-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多