Multiple inheritance and multiple proxies are not supported at the language level of Swift, but we sometimes run into problems like:
- 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 class
God Class
It can be difficult to maintain. - 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 inheritanceUIView
Extension 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