协议(Protocol)
协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)
protocol Test {func play()var x: Int { get set}var y: Int { get }subscript(index: Int) -> Int { get set }}
protocol Test1 {}protocol Test2 {}protocol Test3 {}class Test : Test1, Test2, Test3 {}
协议中定义方法时不能有默认参数值
默认情况下,协议中定义的内容必须全部都实现(也有办法办到只实现部分内容)
协议中的属性
protocol Test {func play()var x: Int { get set}var y: Int { get }subscript(index: Int) -> Int { get set }}class Person : Test {// 通过存储属性实现协议的属性var x: Int = 0var y: Int = 0func play() {}subscript(index: Int) -> Int {set {}get { return index }}}class Student : Test {// 通过计算属性实现协议的属性var x: Int{get { 0 }set { }}var y: Int {get { 0 }}func play() {}subscript(index: Int) -> Int {set {}get { return index }}}
- 协议中定义属性必须用var关键字
- 实现协议时的属性权限要不小于协议中定义的属性权限:
协议定义get、set,用var存储属性或get、set计算属性去实现
协议定义get,用任何属性都可以实现
static、class
为了保证通用,协议中必须使用static定义类型方法、类型属性、类型下标
protocol Test1 {static func play()}class Person : Test1 {static func play() {}}struct Student: Test1 {static func play() {}}class Dog : Test1 {class func play() {}}
为了保证类、结构体、枚举都能实现协议中的类型方法,所以需要使用static修饰类型方法。
类中实现类型方法时,可以使用static也可以使用class,看是否允许子重写。
mutating
只有将协议中的实例方法标记为mutating:
才允许结构体、枚举的具体实现修改自身内存
类在实现方法时不用加mutating,枚举、结构体才需要加mutating
protocol Test {mutating func play()}class Person : Test {var width: Int = 0func play() {width = 10}}struct Student : Test {var x: Int = 0mutating func play() {x = 10}}
init
协议中还可以定义初始化器init
非final类实现时必须加上required
protocol Test {init(x: Int, y: Int)}class Person : Test {required init(x: Int, y: Int) {}}final class Student : Test {init(x: Int, y: Int) {}}
之所以非final类实现协议中初始化器需要加上required,是因为保证子类也需要实现该方法。
如果从协议实现的初始化器,刚好时重写了父类的指定初始化器,那么初始化必须同时加required、override
protocol Test {init(age: Int)}class Person {init(age: Int) {}}class Student: Person, Test {required override init(age: Int) {super.init(age: age)}}
init、init?、init!
协议中定义的init?、init!,可以用init、init?、init!去实现
协议中定义的init,可以用init、init!去实现
protocol Test {init()init?(age: Int)init!(name: String)}class Person : Test {// 实现 init()required init() {}// 或 required init!() {}// 实现 init?(age: Int)required init?(age: Int) {}// 或 required init!(age: Int) {}// 或 required init(age: Int) {}// 实现 init!(name: String)required init!(name: String) {}// 或 required init?(name: String) {}// 或 required init(name: String) {}}
协议的继承
一个协议可以继承其他协议
protocol Sport {func run()}protocol Music : Sport {func sing()}class Person : Music {func run() {}func sing() {}}
协议组合
协议组合,可以包含1个类类型(最多1个)
protocol Sport { }protocol Music { }class Person { }// 接收Person或者其子类的实例func fn1(obj: Person) {}// 接收遵守Sport协议的实例func fn2(obj: Sport) {}// 接收同时遵守Sport和Music协议的实例func fn3(obj: Sport & Music) {}// 接收同时遵守Sport和Music协议、并且是Person或者其子类的实例func fn4(obj: Person & Sport & Music) {}// 也可以设置别名typealias NewPerson = Person & Sport & Music// 接收同时遵守Sport和Music协议、并且是Person或者其子类的实例func fn5(obj: NewPerson) {}
CaseInterable
让枚举遵守CaseIterable协议,可以实现遍历枚举
enum Season : CaseIterable {case springcase summercase autumncase winter}let seasons = Season.allCases // [Season]for season in seasons {print(season)}// spring summer autumn winter
遵守CaseIterable协议后,可以使用allCases遍历枚举。
CustomStringConvertible
继承CustomStringConvertible协议后,可以重写description计算属性,在打印实例对象时,就会输出description的内容
class Person : CustomStringConvertible {var age: Int = 0var description: String {get {"I am a person, age \(age)."}}}var p = Person()print(p)
CustomDebugStringConvertible协议和CustomStringConvertible类似:
class Person : CustomDebugStringConvertible {var age: Int = 0var debugDescription: String {get {"I am a person, age \(age)."}}}var p = Person()debugPrint(p)
print调用的是CustomStringConvertible协议的description
debugPrint、po调用的是CustomDebugStringConvertible协议的debugDescription
Any、AnyObject
Swift提供了两种特殊的类型:Any、AnyObject
Any:可以代表任意类型(枚举、结构体、类,也包括函数类型)
AnyObject:可以代表任意类类型(在协议后面写上:AnyObject代表只有类能遵守这个协议)
Any类型的变量,赋值不同类型不会报错
var num: Any = 10num = "10"num = NSObject()
创建1个能存放任意类型的数组
var arr = [Any]() // 或var arr = Array<Any>()arr.append(1)arr.append(0.5)arr.append("abc")arr.append({ 10 })// 闭包表达式
is、as?、as!、as
is用来判断是否为某种类型,as用来做强制类型转换
is使用:
protocol Sport { func run() }class Person {}class Student : Person, Sport {func run() {}func study() {}}var stu: Any = 1print(stu is Int) // truestu = "Jack"print(stu is String) // truestu = Student()print(stu is Student) // trueprint(stu is Person) // trueprint(stu is Sport) // true
as?和as!使用:
var stu: Any = 10(stu as? Student)?.study() // 没有调用study()//(stu as! Student).study() // 隐式解包会导致报错stu = Student()(stu as? Student)?.study() // 调用了study()(stu as! Student).study() // 调用了study()(stu as? Sport)?.run() // 调用了run()
as使用:
var data = [Any]()data.append(Int("123") as Any)
*肯定能转换成功的情况下使用as
X.self、X.Type、AnyClass
X.self是一个元类型(metadata)的指针,metadata存放着类型相关信息
X.self属于X.Type类型
class Person {}class Student : Person {}var pType: Person.Type = Person.selfvar sType: Student.Type = Student.selfpType = Student.self
*最后一句成立是因为Student继承与Person,所以Student.self也属于Person.Type
AnyClass
// AnyClass的系统定义public typealias AnyClass = AnyObject.Type
// 因为AnyObject代表任意类,所以AnyObject.Type可以是所有类.self的类型var anyType1: AnyObject.Type = Person.selfanyType1 = Student.self// AnyClass和AnyObject.Type的意义一样var anyType2: AnyClass = Person.selfanyType2 = Student.self
类型判断
var p = Person()var pType = type(of: p) // Person.selfprint(Person.self == type(of: p)) // true
*type(of: ) 的本质是读取对象的前8个字节出来,底层不是函数
元类型的应用
1、通过元类型快速创建子类实例对象
class Animal {required init() {}}class Dog : Animal {}class Cat : Animal {}class Pig : Animal {}func create(_ clsArr: [Animal.Type]) -> [Animal] {var arr = [Animal]()for cls in clsArr {arr.append(cls.init())}return arr}print(create([Cat.self, Dog.self, Pig.self]))
打印结果:
[SwiftTest.Cat, SwiftTest.Dog, SwiftTest.Pig]
2、通过元类型获取父类
class Person {}class Student : Person {}print(class_getSuperclass(Student.self)!) // Personprint(class_getSuperclass(Person.self)!) // _TtCs12_SwiftObject
从结果可以看出来,Swift还有个隐藏的基类SwiftObject,可以通过Swift源码查看:https://github.com/apple/swift/blob/main/stdlib/public/runtime/SwiftObject.h
#if SWIFT_OBJC_INTEROP#if __OBJC__// Source code: "SwiftObject"// Real class name: mangled "Swift._SwiftObject"#define SwiftObject _TtCs12_SwiftObject#if __has_attribute(objc_root_class)__attribute__((__objc_root_class__))#endifSWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {@privateClass isa;SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;}
Self
Self一般用作返回值类型,限定返回值跟方法调用者必须是同一种类型(也可以作为参数类型)
protocol Test {func test() -> Self}class Person : Test {required init() {}func test() -> Self {type(of: self).init()}}class Student : Person {}var p = Person()print(p.test()) // Personvar stu = Student()print(stu.test()) // Student
如果Self用在类中,要求返回时调用的初始化器是required的
Self代表当前类型
class Person {var age = 1static var count = 2func run() {// 小写selfprint(self.age) // 1// 大写Selfprint(Self.count) // 2}}
