多繼承和多重代理在swift的語言層面上是不支持的,但我們有時會遇到這樣的問題:
- 類B和C分別繼承自A,B1和B2繼承自B,C1和C2繼承自C.現(xiàn)在我們需要在B1和C1中添加相同的方法,怎么去做?使用繼承的話只能在類A中添加,但這樣做的結果是基類A會越來越臃腫,最后變成上帝類
God Class
,維護起來會很困難. - 在實現(xiàn)完某個代理后發(fā)現(xiàn),我們還要在其他頁面中獲取數(shù)據(jù).例如,IM消息接收之后要在多個地方做回調,比如顯示消息內容頁面,改變小紅點,顯示消息數(shù).即一對多的模式,我們第一反應是用通知,但通知還是能少用就少用,用多了代碼的可閱讀性會大大降低.
面對第一種情況,最好的解決方法是,B1和C1的公共方法專門封裝到一個地方,需要的時候就調用一下,多繼承就是一個最好的解決方案.
1. 多繼承
1. 實現(xiàn)過程
swift中的類可以遵守多個協(xié)議,但是只可以繼承一個類,而值類型(結構體和枚舉)只能遵守單個或多個協(xié)議,不能做繼承操作.
多繼承的實現(xiàn):協(xié)議的方法可以在該協(xié)議的extension
中實現(xiàn)
protocol Behavior {
func run()
}
extension Behavior {
func run() {
print("Running...")
}
}
struct Dog: Behavior {}
let myDog = Dog()
myDog.run() // Running...
無論是結構體還是類還是枚舉都可以遵守多個協(xié)議,所以要實現(xiàn)多繼承,無非就是多遵守幾個協(xié)議的問題.
下面舉個例子.
2. 通過多繼承為UIView
擴展方法
// MARK: - 閃爍功能
protocol Blinkable {
func blink()
}
extension Blinkable where Self: UIView {
func blink() {
alpha = 1
UIView.animate(
withDuration: 0.5,
delay: 0.25,
options: [.repeat, .autoreverse],
animations: {
self.alpha = 0
})
}
}
// MARK: - 放大和縮小
protocol Scalable {
func scale()
}
extension Scalable where Self: UIView {
func scale() {
transform = .identity
UIView.animate(
withDuration: 0.5,
delay: 0.25,
options: [.repeat, .autoreverse],
animations: {
self.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
})
}
}
// MARK: - 添加圓角
protocol CornersRoundable {
func roundCorners()
}
extension CornersRoundable where Self: UIView {
func roundCorners() {
layer.cornerRadius = bounds.width * 0.1
layer.masksToBounds = true
}
}
extension UIView: Scalable, Blinkable, CornersRoundable {}
cyanView.blink()
cyanView.scale()
cyanView.roundCorners()
這樣,如果我們自定義了其他View,只需要放大和縮小效果,遵守Scalable
協(xié)議就可以啦!
3. 多繼承鉆石問題(Diamond Problem),及解決辦法
請看下面代碼
protocol ProtocolA {
func method()
}
extension ProtocolA {
func method() {
print("Method from ProtocolA")
}
}
protocol ProtocolB {
func method()
}
extension ProtocolB {
func method() {
print("Method from ProtocolB")
}
}
class MyClass: ProtocolA, ProtocolB {}
此時ProtocolA
和ProtocolB
都有一個默認的實現(xiàn)方法method()
,由于編譯器不知道繼承過來的method()
方法是哪個,就會報錯.
??鉆石問題
Diamond Problem
,當某一個類或值類型在繼承圖譜中有多條路徑時就會發(fā)生.
解決方法:
1. 在目標值類型或類中重寫那個發(fā)生沖突的方法method()
.
2. 直接修改協(xié)議中重復的方法.
文章開頭我們提到的問題2,我們可以試著用多重代理去解決這個問題.
2. 多重代理
1. 多重代理的實現(xiàn)過程
我們以一個代理的經(jīng)典問題來表述:
主人叫寵物們去吃飯,吃這個動作作為一個協(xié)議,我們要做到統(tǒng)一管理.
1. 定義協(xié)議
protocol MasterOrderDelegate: class {
func toEat(_ food: String)
}
2. 定義一個類: 用來管理遵守協(xié)議的類
這邊用了NSHashTable
來存儲遵守協(xié)議的類,NSHashTable
和NSSet
類似,但又有所不同,總的來說有這幾個特點:
1. NSHashTable
中的元素可以通過Hashable
協(xié)議來判斷是否相等.
2. NSHashTable
中的元素如果是弱引用,對象銷毀后會被移除,可以避免循環(huán)引用.
class masterOrderDelegateManager : MasterOrderDelegate {
private let multiDelegate: NSHashTable<AnyObject> = NSHashTable.weakObjects()
init(_ delegates: [MasterOrderDelegate]) {
delegates.forEach(multiDelegate.add)
}
// 協(xié)議中的方法,可以有多個
func toEat(_ food: String) {
invoke { $0.toEat(food) }
}
// 添加遵守協(xié)議的類
func add(_ delegate: MasterOrderDelegate) {
multiDelegate.add(delegate)
}
// 刪除指定遵守協(xié)議的類
func remove(_ delegateToRemove: MasterOrderDelegate) {
invoke {
if $0 === delegateToRemove as AnyObject {
multiDelegate.remove($0)
}
}
}
// 刪除所有遵守協(xié)議的類
func removeAll() {
multiDelegate.removeAllObjects()
}
// 遍歷所有遵守協(xié)議的類
private func invoke(_ invocation: (MasterOrderDelegate) -> Void) {
for delegate in multiDelegate.allObjects.reversed() {
invocation(delegate as! MasterOrderDelegate)
}
}
}
3. 其余部分
class Master {
weak var delegate: MasterOrderDelegate?
func orderToEat() {
delegate?.toEat("meat")
}
}
class Dog {}
extension Dog: MasterOrderDelegate {
func toEat(_ food: String) {
print("\(type(of: self)) is eating \(food)")
}
}
class Cat {}
extension Cat: MasterOrderDelegate {
func toEat(_ food: String) {
print("\(type(of: self)) is eating \(food)")
}
}
let cat = Cat()
let dog = Dog()
let cat1 = Cat()
let master = Master()
// master的delegate是弱引用,所以不能直接賦值
let delegate = masterOrderDelegateManager([cat, dog])
// 添加遵守該協(xié)議的類
delegate.add(cat1)
// 刪除遵守該協(xié)議的類
delegate.remove(dog)
master.delegate = delegate
master.orderToEat()
// 輸出
// Cat is eating meat
// Cat is eating meat
設置masterOrderDelegateManager
的好處是,可以通過一個數(shù)組來管理多重代理.
更多iOS相關知識點歡迎關注我的Github: SwiftTips