如何定义可选的protocol属性和方法呢?
2018-03-16 14:36
429 查看
定义可选的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可以被继承,struct、class、enum都可以同事遵守多个约束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()注意:当我们在结构体中的方法修改到属性的时候需要在方法前面加上关键字
协议中的初始化器我们可以在协议中定义遵循协议的类型需要实现的指定初始化器(构造函数)或者便捷初始化器。
protocol Pet { init(name:String)}class Cat: Pet { var name:String = "Cat" required init(name: String) { self.name = name }}
//上面的调用肯定是无法通过的,因为编译器无法知道同名text()方法到底是哪个协议中的方法,那么出现这种情况的根本原因在于调用哪个协议的text()不确定,因此我们需要指定调用特定协议的text()方法,改进后的代码如下
let num = (p1 as OneProtocol).text() //10let string = (p1 as TwoProtocol).text() //"hello"//也可以理解为在进行调用前将p1常量进行类型转换。协议的聚合//协议聚合成临时的类型typealias threeProtocol = OneProtocol & TwoProtocol//协议聚合成为参数的类型func text(paramter:OneProtocol & TwoProtocol){继承和聚合在使用上的区别善于思考的同学可以发现,要实现上面的
如何让定义的协议只能被类遵守?:使用关键字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可以自定义输出的参数,这样在调试的时候就会方便很多,好处还有不少,就不一一列举.
class xiaoMing:StudentProtocol{ //定义可选的约束方法和属性,需要定义@objc的protocol,而且每个可选属性或方法前面也要加 @objc //这种类型的约束,只能被 class 遵守}protocol可以被继承,struct、class、enum都可以同事遵守多个约束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关键字修饰初始化器的具体实现。多个协议重名方法调用冲突protocol OneProtocol{ func text() -> Int}protocol TwoProtocol { 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() //10let 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可以自定义输出的参数,这样在调试的时候就会方便很多,好处还有不少,就不一一列举.
相关文章推荐
- ExtJS学习笔记(一):ExtJS程序的结构及如何定义js的private, public属性、方法
- 如何在C#中使用get及set方法定义属性
- 如何在Java类中定义接口属性并如何使用接口的方法
- S4.1_Struts2_ActionAdvance 在action动作类里定义增删改方法,在struts.xml中的action method属性如何设置?
- JavaScript面向对象(1)--属性和方法的定义
- C#派生类中如何定义属性控制基类事件,通过派生类属性直接操作基类事件
- 如何自定义JSTL标签与SpringMVC 标签的属性中套JSTL标签报错的解决方法
- liferay中如何实现自己定义的方法
- 用css定义一个div,如何用js取得样式的属性呢?
- 如何定义一个getter和seter设置的属性可以被绑定
- c# 类成员的定义 定义方法、字段和属性
- 整理的HTML5 CANVAS 定义、属性和方法
- 在实体类中对与记录数量的属性进行修改时,定义相关方法在实体类中,进行修改时方便
- 牛客网Java刷题知识点之什么是内部类、为什么要使用内部类、内部类如何使用外部类的属性和方法、成员内部类、局部内部类、静态内部类、匿名内部类
- 定义员工类employee ,员工有姓名,年龄,工号属性, 员工有工作的方法, 键盘录入三组数据, 数据格式(姓名,年龄,工号) 例如:张三,23,9527; 通过数据,创建员工对象, 然后通过对
- 如何绕过客户化重载方法返回原始定义的方法(AtClass)
- WPF自制控件如何定义依赖属性
- 图像处理工具包ImagXpress中如何定义查看器的属性
- javascript构造函数类和原型prototype定义的属性和方法的区别
- Android如何生成公共属性的get,set方法时,去除 成员变量的m前缀