协议(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 = 0
var y: Int = 0
func 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 = 0
func play() {
width = 10
}
}
struct Student : Test {
var x: Int = 0
mutating 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 spring
case summer
case autumn
case 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 = 0
var description: String {
get {
"I am a person, age \(age)."
}
}
}
var p = Person()
print(p)
CustomDebugStringConvertible协议和CustomStringConvertible类似:
class Person : CustomDebugStringConvertible {
var age: Int = 0
var 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 = 10
num = "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 = 1
print(stu is Int) // true
stu = "Jack"
print(stu is String) // true
stu = Student()
print(stu is Student) // true
print(stu is Person) // true
print(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.self
var sType: Student.Type = Student.self
pType = Student.self
*最后一句成立是因为Student继承与Person,所以Student.self也属于Person.Type
AnyClass
// AnyClass的系统定义
public typealias AnyClass = AnyObject.Type
// 因为AnyObject代表任意类,所以AnyObject.Type可以是所有类.self的类型
var anyType1: AnyObject.Type = Person.self
anyType1 = Student.self
// AnyClass和AnyObject.Type的意义一样
var anyType2: AnyClass = Person.self
anyType2 = Student.self
类型判断
var p = Person()
var pType = type(of: p) // Person.self
print(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)!) // Person
print(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__))
#endif
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
@private
Class 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()) // Person
var stu = Student()
print(stu.test()) // Student
如果Self用在类中,要求返回时调用的初始化器是required的
Self代表当前类型
class Person {
var age = 1
static var count = 2
func run() {
// 小写self
print(self.age) // 1
// 大写Self
print(Self.count) // 2
}
}