Multiple inheritance and multiple proxies are not supported at the language level of Swift, but we sometimes run into problems like:

  1. Classes B and C inherit from A,B1 and B2 from B, and C1 and C2 from C, respectively. Now we need to add the same method to B1 and C1. How do we do that? Using inheritance, you can only add it to class A, but the result is that base class A gets bloated and becomes god classGod ClassIt can be difficult to maintain.
  2. After implementing an agent, we find that we need to fetch data in other pages. For example, after an IM message is received, callbacks are made in various places, such as displaying the message content page, changing the red dot, and displaying the number of messages. In one-to-many mode, our first reaction is to use notifications, but we still use them as little as possible, and the code becomes much less readable.

In the first case, the best solution is for B1 and C1’s public methods to be wrapped exclusively in one place and called when needed. Multiple inheritance is the best solution.

1. Multiple inheritance

1. Implementation process

Classes in SWIFT can adhere to multiple protocols but can only inherit from one class, while value types (structs and enumerations) adhere to one or more protocols and cannot inherit from them.

Implementation of multiple inheritance: Methods of the protocol can be implemented in extension of the protocol

protocol Behavior {
    func run(a)
}
extension Behavior {
    func run(a) {
        print("Running...")}}struct Dog: Behavior {}

let myDog = Dog()
myDog.run() // Running...
Copy the code

Structs, classes, and enumerations can adhere to multiple protocols, so implementing multiple inheritance is simply a matter of adhering to more than one protocol.

Here’s an example.

2. Through multiple inheritanceUIViewExtension methods

// MARK: - Flicker
protocol Blinkable {
    func blink(a)
}
extension Blinkable where Self: UIView {
    func blink(a) {
        alpha = 1
        
        UIView.animate(
            withDuration: 0.5,
            delay: 0.25,
            options: [.repeat, .autoreverse],
            animations: {
                self.alpha = 0}}})// MARK: - Zoom in and out
protocol Scalable {
    func scale(a)
}
extension Scalable where Self: UIView {
    func scale(a) {
        transform = .identity
        
        UIView.animate(
            withDuration: 0.5,
            delay: 0.25,
            options: [.repeat, .autoreverse],
            animations: {
                self.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)}}}// MARK: - Add rounded corners
protocol CornersRoundable {
    func roundCorners(a)
}
extension CornersRoundable where Self: UIView {
    func roundCorners(a) {
        layer.cornerRadius = bounds.width * 0.1
        layer.masksToBounds = true}}extension UIView: Scalable.Blinkable.CornersRoundable {}

 cyanView.blink()
 cyanView.scale()
 cyanView.roundCorners()
Copy the code

This way, if we customize other views, we just need to zoom in and out and follow the Scalable protocol!

3. Inherit the Diamond Problem and its solution

Look at the following code

protocol ProtocolA {
    func method(a)
}

extension ProtocolA {
    func method(a) {
        print("Method from ProtocolA")}}protocol ProtocolB {
    func method(a)
}

extension ProtocolB {
    func method(a) {
        print("Method from ProtocolB")}}class MyClass: ProtocolA.ProtocolB {}
Copy the code

ProtocolA and ProtocolB both have a default implementation method(), and an error is reported because the compiler does not know which method() is inherited.

💎 Diamond Problem Diamond Problem occurs when a class or value type has multiple paths in the inheritance graph.

Override method() in the target value type or class. 2. Modify duplicate methods in the protocol directly.

Problem 2 we mentioned at the beginning of this article, we can try to solve this problem with multiple agents.

2. Multiple agents

1. Realization process of multiple agents

We express it with a classic problem of agency: the owner calls the pets to have a meal, and eating this action is an agreement that we should manage uniformly.

1. Define the agreement
protocol MasterOrderDelegate: class {
    func toEat(_ food: String)
}
Copy the code
2. Define a class: a class that manages compliance

NSHashTable is used to store classes that comply with the protocol. NSHashTable is similar to NSSet, but different from NSSet. In general, it has the following characteristics: Elements in NSHashTable can be determined by Hashable protocol to determine whether they are equal. 2. If elements in NSHashTable are weak references, they will be removed after the object is destroyed to avoid circular references.

class masterOrderDelegateManager : MasterOrderDelegate {
    private let multiDelegate: NSHashTable<AnyObject> = NSHashTable.weakObjects()

    init(_ delegates: [MasterOrderDelegate]) {
        delegates.forEach(multiDelegate.add)
    }
    
    // There can be multiple methods in the protocol
    func toEat(_ food: String) {
        invoke { $0.toEat(food) }
    }
    
    // Add classes that comply with the protocol
    func add(_ delegate: MasterOrderDelegate) {
        multiDelegate.add(delegate)
    }
    
    // Delete the classes specified to comply with the protocol
    func remove(_ delegateToRemove: MasterOrderDelegate) {
        invoke {
            if $0 === delegateToRemove as AnyObject {
                multiDelegate.remove($0)}}}// Delete all classes that comply with the protocol
    func removeAll(a) {
        multiDelegate.removeAllObjects()
    }

    // Iterate over all classes that comply with the protocol
    private func invoke(_ invocation: (MasterOrderDelegate) -> Void) {
        for delegate in multiDelegate.allObjects.reversed() {
            invocation(delegate as! MasterOrderDelegate)}}}Copy the code
3. The rest
class Master {
    weak var delegate: MasterOrderDelegate?
    func orderToEat(a){ 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(a)let dog = Dog(a)let cat1 = Cat(a)let master = Master(a)// The master delegate is a weak reference, so it cannot be directly assigned
let delegate = masterOrderDelegateManager([cat, dog])
// Add classes that comply with the protocol
delegate.add(cat1)
// Delete classes that comply with this protocol
delegate.remove(dog)

master.delegate = delegate
master.orderToEat()

/ / output
// Cat is eating meat
// Cat is eating meat
Copy the code

Set masterOrderDelegateManager advantage is that can pass an array to manage multiple agents.

For more iOS related knowledge, please follow me on Github: SwiftTips