談?wù)刬OS中的多繼承與多重代理
前言
多繼承和多重代理在swift的語(yǔ)言層面上是不支持的,但我們有時(shí)會(huì)遇到這樣的問(wèn)題:
- 類B和C分別繼承自A,B1和B2繼承自B,C1和C2繼承自C.現(xiàn)在我們需要在B1和C1中添加相同的方法,怎么去做?使用繼承的話只能在類A中添加,但這樣做的結(jié)果是基類A會(huì)越來(lái)越臃腫,最后變成上帝類God Class,維護(hù)起來(lái)會(huì)很困難.
- 在實(shí)現(xiàn)完某個(gè)代理后發(fā)現(xiàn),我們還要在其他頁(yè)面中獲取數(shù)據(jù).例如,IM消息接收之后要在多個(gè)地方做回調(diào),比如顯示消息內(nèi)容頁(yè)面,改變小紅點(diǎn),顯示消息數(shù).即一對(duì)多的模式,我們第一反應(yīng)是用通知,但通知還是能少用就少用,用多了代碼的可閱讀性會(huì)大大降低.
面對(duì)第一種情況,最好的解決方法是,B1和C1的公共方法專門(mén)封裝到一個(gè)地方,需要的時(shí)候就調(diào)用一下,多繼承就是一個(gè)最好的解決方案.
1. 多繼承
1. 實(shí)現(xiàn)過(guò)程
swift中的類可以遵守多個(gè)協(xié)議,但是只可以繼承一個(gè)類,而值類型(結(jié)構(gòu)體和枚舉)只能遵守單個(gè)或多個(gè)協(xié)議,不能做繼承操作.
多繼承的實(shí)現(xiàn):協(xié)議的方法可以在該協(xié)議的extension中實(shí)現(xiàn)
protocol Behavior { func run() } extension Behavior { func run() { print("Running...") } } struct Dog: Behavior {} let myDog = Dog() myDog.run() // Running...
無(wú)論是結(jié)構(gòu)體還是類還是枚舉都可以遵守多個(gè)協(xié)議,所以要實(shí)現(xiàn)多繼承,無(wú)非就是多遵守幾個(gè)協(xié)議的問(wèn)題.
下面舉個(gè)例子.
2. 通過(guò)多繼承為UIView擴(kuò)展方法
// 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. 多繼承鉆石問(wèn)題(Diamond Problem),及解決辦法
請(qǐng)看下面代碼
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 {}
此時(shí)ProtocolA和ProtocolB都有一個(gè)默認(rèn)的實(shí)現(xiàn)方法method(),由于編譯器不知道繼承過(guò)來(lái)的method()方法是哪個(gè),就會(huì)報(bào)錯(cuò).
💎鉆石問(wèn)題Diamond Problem,當(dāng)某一個(gè)類或值類型在繼承圖譜中有多條路徑時(shí)就會(huì)發(fā)生.
解決方法:
1. 在目標(biāo)值類型或類中重寫(xiě)那個(gè)發(fā)生沖突的方法method().
2. 直接修改協(xié)議中重復(fù)的方法.
文章開(kāi)頭我們提到的問(wèn)題2,我們可以試著用多重代理去解決這個(gè)問(wèn)題.
2. 多重代理
1. 多重代理的實(shí)現(xiàn)過(guò)程
我們以一個(gè)代理的經(jīng)典問(wèn)題來(lái)表述:
主人叫寵物們?nèi)コ燥?吃這個(gè)動(dòng)作作為一個(gè)協(xié)議,我們要做到統(tǒng)一管理.
1. 定義協(xié)議
protocol MasterOrderDelegate: class { func toEat(_ food: String) }
2. 定義一個(gè)類: 用來(lái)管理遵守協(xié)議的類
這邊用了NSHashTable來(lái)存儲(chǔ)遵守協(xié)議的類,NSHashTable和NSSet類似,但又有所不同,總的來(lái)說(shuō)有這幾個(gè)特點(diǎn):
1. NSHashTable中的元素可以通過(guò)Hashable協(xié)議來(lái)判斷是否相等.
2. NSHashTable中的元素如果是弱引用,對(duì)象銷(xiāo)毀后會(huì)被移除,可以避免循環(huán)引用.
class masterOrderDelegateManager : MasterOrderDelegate { private let multiDelegate: NSHashTable<AnyObject> = NSHashTable.weakObjects() init(_ delegates: [MasterOrderDelegate]) { delegates.forEach(multiDelegate.add) } // 協(xié)議中的方法,可以有多個(gè) 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
設(shè)置masterOrderDelegateManager的好處是,可以通過(guò)一個(gè)數(shù)組來(lái)管理多重代理.
更多iOS相關(guān)知識(shí)點(diǎn)歡迎關(guān)注我的Github: SwiftTips (本地下載)
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
詳解Swift 利用Opration和OprationQueue來(lái)下載網(wǎng)絡(luò)圖片
這篇文章主要介紹了詳解Swift 利用Opration和OprationQueue來(lái)下載網(wǎng)絡(luò)圖片的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09iOS仿網(wǎng)易簡(jiǎn)單頭部滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了iOS仿網(wǎng)易簡(jiǎn)單頭部滾動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-05-05

iOS App中調(diào)用iPhone各種感應(yīng)器的方法總結(jié)

iOS Block解開(kāi)多年以來(lái)一直的誤解

iOS監(jiān)聽(tīng)手機(jī)鎖屏狀態(tài)

iOS開(kāi)發(fā)中的幾個(gè)手勢(shì)操作實(shí)例分享

iOS模仿電子書(shū)首頁(yè)實(shí)現(xiàn)書(shū)架布局樣式