Swift protocol and opaque type
concept
agreement
A protocol defines a collection of interfaces used to declare methods, properties, and other requirements for a particular task or function. In swift syntax, classes, structures, and enumerations can follow the protocol and implement methods in the protocol interface. Protocol plays an important role in interface – oriented, slice – oriented and componentized design. Since there is no multiple inheritance in Objective-C, interface oriented programming is also implemented through protocols.
Opaque type
The return value of a function or method of an opaque type, not a specific type, but a protocol-described type. However, unlike normal return protocol described types, opaque types must be implemented with a unique return type, whereas normal return protocol type methods can return all objects that conform to the protocol.
Usage instructions
The protocol declaration scenario is very similar to that of ordinary classes.
protocol ShapeProctocol {
func drawShape()
}
protocol PatterProctocol {
var name: String? { get }
}
protocol CircularProctocol: ShapeProctocol, PatterProctocol {
func getCircularCenter() -> CGPoint
}
Copy the code
As can be seen from the above code, methods and attributes can be defined when a protocol is declared, and a protocol can follow one or more protocols. Protocol plays an important role in interface oriented programming. Swift’s open source project Swinject uses protocol to decoupled components. OC and SWIFT have no abstract classes in a strict sense, so protocol plays this role.
In iOS development, the protocol-proxy pattern is also often used in one-to-one value passing, function method callbacks, and the protocol-proxy pattern is more centralized through method callbacks than blocks. Many UIKit frameworks use this approach, for example: Note that most delegate properties are declared with the weak modifier to prevent memory leaks in ARC environments, while the CAAnimation’s delegate is specified with the strong modifier.
/* The delegate of the animation. This object is retained for the
* lifetime of the animation object. Defaults to nil. See below for the
* supported delegate methods. */
@property(nullable, strong) id <CAAnimationDelegate> delegate;
Copy the code
When you talk about protocols versus opaque types, in general, you’re talking about how they differ as return values, or what kind of design holes they fill. Using the protocol as the return value maximizes the abstraction and generality of the interface, but at the same time, the return type is uncertain, which means that many operations that depend on the return type information cannot be performed. For example, 🌰 :
protocol TravelToolType { /* ... */ }
struct TheTaxiCar: TravelToolType {
var carNumber: Int
}
struct TheSubWay: TravelToolType { /* ... */ }
public func favoriteTravelTool(withNumber travelNumber: Int) -> TravelToolType {
if likeTaxi {
return TheTaxiCar(carNumber: travelNumber)
}
return TheSubWay()
}
Copy the code
This implementation method, the interface does not disclose the implementation method, as long as the caller is clear about the protocol defined methods, according to the need to use, similar to an abstract project mode, very easy to use. But once our return object needs to be compared using ==, things change:
let weekTravel = favoriteTravelTool(withNumber: 100)
let workTravel = favoriteTravelTool(withNumber: 100)
print(weekTravel == workTravel) // Binary operator '==' cannot be applied to two 'TravelToolType' operands
Copy the code
This error can occur for many reasons, but the immediate cause of the error message is that there is no declaration for the Equatable protocol, so we tried to add the declaration for the Equatable protocol, and the result is as follows:
protocol TravelToolType: Equatable { /* ... */ }
struct TheTaxiCar: TravelToolType {
var carNumber: Int
}
struct TheSubWay: TravelToolType { /* ... */ }
// ---> Protocol 'TravelToolType' can only be used as a generic constraint because it has Self or associated type requirements
public func favoriteTravelTool(withNumber travelNumber: Int) -> TravelToolType {
if likeTaxi {
return TheTaxiCar(carNumber: travelNumber)
}
return TheSubWay()
}
Copy the code
The new problem comes. Here is my personal understanding and explanation. The metaphor may not be accurate enough, so I hope it can be timely supplemented and modified. Since the return protocol is similar to a hidden box, the contents of which are unknown, the use of == in Swift requires knowing exactly whether the contents of the box are cat cans or dog food. In other words, we must know what type of entity is being returned. The problem arises because the type inside is not yet determined.
In the introduction to the concept of opaque types, we said that an opaque type returns a certain protocol type. Is also a hidden box, we do not know what it contained, but once you add some keywords, is similar to production out of the box with tags, even though we still don’t know what is in the box, but we know that this production line can only produce a single type of a product, for instance, They can only produce canned cat food or dog food. Therefore, using an opaque type as the return type makes it possible to explicitly express the REQUIRED API contract. The complete code after modification is as follows:
protocol TravelToolType: Equatable { /* ... */ }
struct TheTaxiCar: TravelToolType {
var carNumber: Int
}
struct TheSubWay: TravelToolType { /* ... */ }
public func favoriteTravelTool(withNumber travelNumber: Int) -> some TravelToolType {
return TheTaxiCar(carNumber: travelNumber)
}
let weekTravel = favoriteTravelTool(withNumber: 100)
let workTravel = favoriteTravelTool(withNumber: 100)
print(weekTravel == workTravel)
* true *\
Copy the code
reference
Nuggets: Opaque types and some keywords in Swift
SwiftGG: Opaque type
New feature for Swift 5.1: Opaque return types