Swift override function in extension
If I have a class:
class Spaceship<FuelType> {
function prepareForLiftoff() throws {
//Start the countdown!
}
}
I originally assumed that I would be able to override prepareForLiftoff
without subclassing by adding an extension:
extension Spaceship where FuelType: CollectionType {
func prepareForLiftoff() throws {}
}
This code doesn't compile though, the error says invalid redeclaration
of the function, which makes sense.
My question is: Is there anyway to override a function of a particular class? In other words can I replace the functionality under certain conditions like the example above where FuelType: CollectionType
. If not, is there any other workaround or way to achieve that behavior (maybe declaring another protocol, idk)
Now that I think about it more, I would have to say that's not possible because what's to stop someone from overriding any of the standard library functions?
From the documentation:
Extensions can add new functionality to a type, but they cannot override existing functionality.
The documentation lists carefully and precisely what an extension is allowed to do.
As to your question:
Is there anyway to override a function of a particular class
Yes, it's called subclassing.
Instead of overriding, you may like to try swizzling. For example the following code allows you to run your own viewWillAppear
. Swift 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)")
}
}
Update 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()
}
}