定义可选的protocol属性或者方法
@objc protocol StudentProtocol{
@objc optional var height: Int{ get set}
@objc optional var weight: Int{ get }
@objc optional func getName()
@objc optional func getSex()
@objc optional func getGrade(grade:Int)
}
class xiaoMing:StudentProtocol{
//定义可选的约束方法和属性,需要定义@objc的protocol,而且每个可选属性或方法前面也要加 @objc
//这种类型的约束,只能被 class 遵守
}
protocol StudentProtocol{
var height: Int{ get set}
var weight: Int{ get }
func getName()
func getSex()
func getGrade(grade:Int)
}
protocol PresonProtocol {
var address:String{get set}
}
struct Person:StudentProtocol,PresonProtocol {
var height: Int
var weight: Int
func getName() {
}
func getSex() {
}
func getGrade(grade: Int) {
}
var address: String
}
协议中定义方法
和objective-c类似,Swift中的协议可以定义类型方法或者实例方法,方法的参数不能有默认值(Swift认为默认值也是一种变相的实现),在遵守该协议的类型中具体实现方法的细节,通过类或实例调用:
protocol Student {
//类方法
static func study()
//实例方法
func changeName()
}
struct CollageStudent: Student {
//类方法实现
static func study() {
}
//实例方法实现
func changeName() {
}
}
//方法的调用
CollageStudent.study()
var c1 = CollageStudent()
c1.changeName()
注意:当我们在结构体中的方法修改到属性的时候需要在方法前面加上关键字mutating
表示该属性能够被修改(如果是类不需要添加mutating
关键字),这样的方法叫做:异变方法,和 “在实例方法中修改值类型” 的处理是一样的。
protocol Student {
var name:String {get}
mutating func changeName()
}
struct CollageStudent: Student {
var name: String
mutating func changeName() {
self.name = "小明"
}
}
var c1 = CollageStudent(name: "小芳")
print(c1) //CollageStudent(name: "小芳")
c1.changeName()
print(c1) //CollageStudent(name: "小明")
协议中的初始化器
我们可以在协议中定义遵循协议的类型需要实现的指定初始化器(构造函数)或者便捷初始化器。
protocol Pet {
init(name:String)
}
class Cat: Pet {
var name:String = "Cat"
required init(name: String) {
self.name = name
}
}
Cat
由于遵循了Pet
协议,应该用required
关键字修饰初始化器的具体实现。
多个协议重名方法调用冲突
func text() -> Int
}
func text() -> String
}
struct Person: OneProtocol,TwoProtocol{
func text() -> Int {
return 10
}
func text() -> String {
return "hello"
}
}
let p1 = Person()
//let num = p1.text() // ❌ Ambiguous use of 'text()'
//let string = p1.text() // ❌ Ambiguous use of 'text()'
//上面的调用肯定是无法通过的,因为编译器无法知道同名text()方法到底是哪个协议中的方法,那么出现这种情况的根本原因在于调用哪个协议的text()不确定,因此我们需要指定调用特定协议的text()方法,改进后的代码如下
let num = (p1 as OneProtocol).text() //10
let string = (p1 as TwoProtocol).text() //"hello"
//也可以理解为在进行调用前将p1常量进行类型转换。
协议的聚合
//协议聚合成临时的类型
typealias threeProtocol = OneProtocol & TwoProtocol
//协议聚合成为参数的类型
func text(paramter:OneProtocol & TwoProtocol){
继承和聚合在使用上的区别
善于思考的同学可以发现,要实现上面的 paramter参数的类型是遵守OneProtocol 和 TwoProtoco
的效果,完全可以使用协议的继承,新定义一个协议ThreeProtocol
继承自OneProtocol
和TwoProtocol
,然后指定paramter
参数的类型是ThreeProtocol
类型。那么这两种方法有何区别呢?首先协议的继承是定义了一个全新的协议,我们是希望它能够“大展拳脚”得到普遍使用。而协议的聚合不一样,它并没有定义新的固定协议类型,相反,它只是定义一个临时的拥有所有聚合中协议要求组成的局部协议,很可能是“一次性需求”,使用协议的聚合保持了代码的简洁性、易读性,同时去除了定义不必要的新类型的繁琐,并且定义和使用的地方如此接近,见明知意,也被称为匿名协议聚合。但是使用了匿名协议聚合能够表达的信息就少了一些,所以需要开发者斟酌使用。
协议的检查
如何检查某个类型是否遵循了特定的协议?:使用关键字 is,同时该运算符会返回一个Bool值用于判断条件是否成立。
struct Person: OneProtocol {
}
let p1 = Person()
if (p1 is OneProtocol){ //可以理解为:p1 是一个遵守了OneProtocol协议类型的实例
print("yes")
}
如何让定义的协议只能被类遵守?:使用关键字class,该关键字修饰之后表示协议只能被类遵守,如果有枚举或结构体尝试遵守会报错。
//只能被类遵守的协议
protocol FourProtocol: class , threeProtocol{
}
//此处报错
//struct Person: FourProtocol {
//}
class People: FourProtocol {
func textOne() -> Int {
return 10
}
func textTwo() -> String {
return "123"
}
}
// 关联类型
//协议的关联类型指的是根据使用场景的变化,如果协议中某些属性存在“逻辑相同而类型不同”的情况,可以使用关键字 associatedtype来为这些属性的类型声明“关联类型”
protocol WeightCalculable {
associatedtype WeightType
var weight:WeightType {get}
}
//WeightCalculable 是一个“可称重”协议, weight属性返回遵守该协议具体类型的实例的重量。在这里我们使用associatedtype为该属性的类型定义了一个别名WeightType,换言之WeightCalculable中并不关心weight的类型是Int 还是Double还是其他什么,他只是简单的告诉我们返回的类型是WeightType,至于WeightType到底是什么类型右遵守该协议的类中自己去定义。那么这样做的好处是什么呢?
//定义手机的结构体
struct HuaweiPhone:WeightCalculable{
typealias WeightType = Double
var weight: WeightType
}
let huaweiP9 = HuaweiPhone(weight: 0.255)
//定义汽车的结构体
struct Car: WeightCalculable{
typealias WeightType = Int
var weight: WeightType
}
let truck = Car(weight: 300_000)
print(truck)//Car(weight: 300000)
//如上所叙:HuaweiPhone,Car 类型都遵守了WeightCalculable协议,都能被称重,各自具体的类型中定义了weight的类型,返回不同类型的值,上面例子中反应出称重逻辑是一样的,但是对于weight属性的返回值要求不一样,如果仅仅是因为返回值类型不一样,而其定义两个不同的协议,这样做显然不适合。所以associatedtype在这种情况下就能发挥出作用了
Swift标准库协议--CustomStringConvertible协议
在调试的时候总会发现在输出自定义的类与结构体时,会打印很多不想输出的变量,这就有了CustomStringConvertible,CustomDebugStringConvertible这两个协议的用处.
struct Person{
var name:String
var age:Int
var sex:String
}
let person1 = Person.init(name: "xiaoMing", age: 20, sex: "man")
print(person1) //Person(name: "xiaoMing", age: 20, sex: "man")
采用协议扩展的方式实现
extension Person:CustomStringConvertible,CustomDebugStringConvertible{
var description: String {
return "\(name) \(age) \(sex)"
}
var debugDescription: String {
return "\(name) \(age) \(sex)"
}
}
let person2 = Person(name: "xiaoFang", age: 20, sex: "women")
print(person2) //xiaoFang 20 women
可以自定义输出的参数,这样在调试的时候就会方便很多,好处还有不少,就不一一列举.