聊聊iOS中的多繼承和多重代理

多繼承和多重代理在swift的語(yǔ)言層面上是不支持的,但我們有時(shí)會(huì)遇到這樣的問(wèn)題:

  1. 類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ì)很困難.
  2. 在實(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的公共方法專門封裝到一個(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()
image

這樣,如果我們自定義了其他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í)ProtocolAProtocolB都有一個(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é)議的類,NSHashTableNSSet類似,但又有所不同,總的來(lái)說(shuō)有這幾個(gè)特點(diǎn):
1. NSHashTable中的元素可以通過(guò)Hashable協(xié)議來(lái)判斷是否相等.
2. NSHashTable中的元素如果是弱引用,對(duì)象銷毀后會(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末螃壤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子筋帖,更是在濱河造成了極大的恐慌奸晴,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件日麸,死亡現(xiàn)場(chǎng)離奇詭異寄啼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)赘淮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門辕录,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人梢卸,你說(shuō)我怎么就攤上這事走诞。” “怎么了蛤高?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵蚣旱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我戴陡,道長(zhǎng)塞绿,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任恤批,我火速辦了婚禮异吻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己诀浪,他們只是感情好棋返,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著雷猪,像睡著了一般睛竣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上求摇,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天射沟,我揣著相機(jī)與錄音,去河邊找鬼与境。 笑死验夯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的摔刁。 我是一名探鬼主播簿姨,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼簸搞!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起准潭,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤趁俊,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后刑然,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體寺擂,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年泼掠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了怔软。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡择镇,死狀恐怖挡逼,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腻豌,我是刑警寧澤家坎,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站吝梅,受9級(jí)特大地震影響虱疏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苏携,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一做瞪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧右冻,春花似錦装蓬、人聲如沸著拭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)茫死。三九已至,卻和暖如春履羞,著一層夾襖步出監(jiān)牢的瞬間峦萎,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工忆首, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爱榔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓糙及,卻偏偏與公主長(zhǎng)得像详幽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浸锨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,405評(píng)論 8 265
  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,160評(píng)論 30 470
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,109評(píng)論 1 32
  • 窗外的路燈準(zhǔn)時(shí)打開(kāi)唇聘,引得幾只蛾子飛舞盤旋,遲遲不走柱搜〕倮桑昏黃的燈光投射上陌桑的側(cè)臉,一點(diǎn)不刺眼聪蘸,她費(fèi)力地?cái)D了擠干澀的眼...
    你就別再想起我閱讀 321評(píng)論 0 2
  • 一 淑芳這一輩子幾乎沒(méi)做過(guò)什么好的夢(mèng)宪肖,年輕的時(shí)候忙東忙西,苦日子似乎沒(méi)有盡頭健爬,整天工作農(nóng)忙控乾,為了養(yǎng)育六個(gè)兒女,她的...
    失眠的陳九閱讀 3,114評(píng)論 31 154