扩展中的Swift覆盖功能

时间:2015-11-23 01:24:58

标签: ios swift generics

如果我有课:

class Spaceship<FuelType> {
    function prepareForLiftoff() throws {
        //Start the countdown!
    }
}

我最初假设我可以通过添加扩展来覆盖prepareForLiftoff而不进行子类化:

extension Spaceship where FuelType: CollectionType {
    func prepareForLiftoff() throws {}
} 

此代码无法编译,错误显示函数的invalid redeclaration,这是有道理的。

我的问题是:无论如何都要覆盖特定类的功能?换句话说,我可以在某些条件下替换功能,例如FuelType: CollectionType上面的示例。如果没有,是否有任何其他解决方法或方法来实现该行为(可能声明另一个协议,idk)

现在我想起来了,我不得不说这是不可能的,因为阻止某人覆盖任何标准库函数的是什么?

2 个答案:

答案 0 :(得分:20)

来自documentation

  

扩展程序可以为类型添加新功能,但它们无法覆盖现有功能。

文档仔细准确地列出了允许扩展的内容。

关于你的问题:

  

无论如何都要覆盖特定类的功能

是的,它被称为子类化。

答案 1 :(得分:1)

您可以尝试调配,而不是覆盖。例如,以下代码允许您运行自己的viewWillAppearSwift 3

extension UIViewController {
    open override class func initialize() {
        // make sure this isn't a subclass
        guard self === UIViewController.self else { return }
        DispatchQueue.once(token: "viewWillAppear") {
            let originalSelector = #selector(self.viewWillAppear(_:))
            let swizzledSelector = #selector(self.proj_viewWillAppear1(animated:))
            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }

    func proj_viewWillAppear1(animated: Bool) {
        self.proj_viewWillAppear1(animated: animated)
        let viewControllerName = NSStringFromClass(type(of: self))
        print("viewWillAppear: \(viewControllerName)")
    }
}

更新20170621

public extension DispatchQueue {
    private static var _onceTracker = [String]()

    public class func once(file: String = #file, function: String = #function, line: Int = #line, block:(Void)->Void) {
        let token = file + ":" + function + ":" + String(line)
        once(token: token, block: block)
    }

    public class func once(token: String, block:(Void)->Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        if _onceTracker.contains(token) {
            return
        }

        _onceTracker.append(token)
        block()
    }
}