经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
Swift 进阶(九)协议、元类型
来源:cnblogs  作者:FunkyRay  时间:2021/4/12 9:50:56  对本文有异议

协议(Protocol)

基本概念

协议可以用来定义方法、属性、下标的声明,协议可以被结构体、类、枚举遵守

  1. protocol Drawable {
  2. func draw()
  3. var x: Int { get set } // get和set只是声明
  4. var y: Int { get }
  5. subscript(index: Int) -> Int { get set }
  6. }

多个协议之间用逗号隔开

  1. protocol Test1 { }
  2. protocol Test2 { }
  3. protocol Test3 { }
  4. class TestClass: Test1, Test2, Test3 { }

协议中定义方法时不能有默认参数值

-w633

默认情况下,协议中定义的内容必须全部都实现

协议中的属性

协议中定义属性必须用var关键字

实现协议时的属性权限要不小于协议中定义的属性权限

  • 协议定义get、set,用var存储属性或get、set计算属性去实现
  • 协议定义get,用任何属性都可以实现
  1. protocol Drawable {
  2. func draw()
  3. var x: Int { get set }
  4. var y: Int { get }
  5. subscript(index: Int) -> Int { get set }
  6. }
  7. class Person1: Drawable {
  8. var x: Int = 0
  9. let y: Int = 0
  10. func draw() {
  11. print("Person1 draw")
  12. }
  13. subscript(index: Int) -> Int {
  14. set { }
  15. get { index }
  16. }
  17. }
  18. class Person2: Drawable {
  19. var x: Int {
  20. get { 0 }
  21. set { }
  22. }
  23. var y: Int { 0 }
  24. func draw() {
  25. print("Person2 draw")
  26. }
  27. subscript(index: Int) -> Int {
  28. set { }
  29. get { index }
  30. }
  31. }
  32. class Person3: Drawable {
  33. var x: Int {
  34. get { 0 }
  35. set { }
  36. }
  37. var y: Int {
  38. get { 0 }
  39. set { }
  40. }
  41. func draw() {
  42. print("Person3 draw")
  43. }
  44. subscript(index: Int) -> Int {
  45. set { }
  46. get { index }
  47. }
  48. }

static、class

为了保证通用,协议中必须用static定义类型方法、类型属性、类型下标

  1. protocol Drawable {
  2. static func draw()
  3. }
  4. class Person1: Drawable {
  5. static func draw() {
  6. print("Person1 draw")
  7. }
  8. }
  9. class Person2: Drawable {
  10. class func draw() {
  11. print("Person2 draw")
  12. }
  13. }

mutating

只有将协议中的实例方法标记为mutating,才允许结构体、枚举的具体实现修改自身内存

类在实现方法时不用加mutating,结构体、枚举才需要加mutating

  1. protocol Drawable {
  2. mutating func draw()
  3. }
  4. class Size: Drawable {
  5. var width: Int = 0
  6. func draw() {
  7. width = 10
  8. }
  9. }
  10. struct Point: Drawable {
  11. var x: Int = 0
  12. mutating func draw() {
  13. x = 10
  14. }
  15. }

init

协议中还可以定义初始化器init,非final类实现时必须加上required

目的是为了让所有遵守这个协议的类都拥有初始化器,所以加上required强制子类必须实现,除非是加上final不需要子类的类

  1. protocol Drawable {
  2. init(x: Int, y: Int)
  3. }
  4. class Point: Drawable {
  5. required init(x: Int, y: Int) {
  6. }
  7. }
  8. final class Size: Drawable {
  9. init(x: Int, y: Int) {
  10. }
  11. }

如果从协议实现的初始化器,刚好是重写了父类的指定初始化器,那么这个初始化必须同时加上required、override

  1. protocol Livable {
  2. init(age: Int)
  3. }
  4. class Person {
  5. init(age: Int) { }
  6. }
  7. class Student: Person, Livable {
  8. required override init(age: Int) {
  9. super.init(age: age)
  10. }
  11. }

协议中定义的init?、init!,可以用init、init?、init!去实现

  1. protocol Livable {
  2. init()
  3. init?(age: Int)
  4. init!(no: Int)
  5. }
  6. class Person1: Livable {
  7. required init() {
  8. }
  9. required init?(age: Int) {
  10. }
  11. required init!(no: Int) {
  12. }
  13. }
  14. class Person2: Livable {
  15. required init() {
  16. }
  17. required init!(age: Int) {
  18. }
  19. required init?(no: Int) {
  20. }
  21. }
  22. class Person3: Livable {
  23. required init() {
  24. }
  25. required init(age: Int) {
  26. }
  27. required init(no: Int) {
  28. }
  29. }

协议中定义的init,可以用init、init!去实现

  1. protocol Livable {
  2. init()
  3. init?(age: Int)
  4. init!(no: Int)
  5. }
  6. class Person4: Livable {
  7. required init!() {
  8. }
  9. required init?(age: Int) {
  10. }
  11. required init!(no: Int) {
  12. }
  13. }

协议的继承

一个协议可以继承其他协议

  1. protocol Runnable {
  2. func run()
  3. }
  4. protocol Livable: Runnable {
  5. func breath()
  6. }
  7. class Person: Livable {
  8. func breath() {
  9. }
  10. func run() {
  11. }
  12. }

协议组合

协议组合可以包含一个类类型

  1. protocol Runnable { }
  2. protocol Livable { }
  3. class Person { }
  4. // 接收Person或者其子类的实例
  5. func fn0(obj: Person) { }
  6. // 接收遵守Livable协议的实例
  7. func fn1(obj: Livable) { }
  8. // 接收同时遵守Livable、Runnable协议的实例
  9. func fn2(obj: Livable & Runnable) { }
  10. // 接收同时遵守Livable、Runnable协议,并且是Person或者其子类的实例
  11. func fn3(obj: Person & Livable & Runnable) { }
  12. typealias RealPerson = Person & Livable & Runnable
  13. func fn4(obj: RealPerson) { }

CaseIterable

让枚举遵守CaseIterable协议,可以实现遍历枚举值

  1. enum Season: CaseIterable {
  2. case spring, summer, autumn, winter
  3. }
  4. let seasons = Season.allCases
  5. print(seasons.count)
  6. for season in seasons {
  7. print(season)
  8. } // spring, summer, autumn, winter

CustomStringConvertible

遵守CustomStringConvertible、CustomDebugStringConvertible协议,都可以自定义实例的打印字符串

  1. class Person: CustomStringConvertible, CustomDebugStringConvertible {
  2. var age = 0
  3. var description: String { "person_\(age)" }
  4. var debugDescription: String { "debug_person_\(age)" }
  5. }
  6. var person = Person()
  7. print(person) // person_0
  8. debugPrint(person) // debug_person_0

print调用的是CustomStringConvertible协议的description

debugPrint、po调用的是CustomDebugStringConvertible协议的debugDescription

-w529

Any、AnyObject

Swift提供了两种特殊的类型Any、AnyObject

Any可以代表任意类型(枚举、结构体、类,也包括函数类型)

  1. var stu: Any = 10
  2. stu = "Jack"
  3. stu = Size()
  1. var data = [Any]()
  2. data.append(1)
  3. data.append(3.14)
  4. data.append(Size())
  5. data.append("Jack")
  6. data.append({ 10 })

AnyObject可以代表任意类类型

在协议后面写上: AnyObject,代表只有类能遵守这个协议

-w644

在协议后面写上: class,也代表只有类能遵守这个协议

-w642

is、as

is用来判断是否为某种类型

  1. protocol Runnable {
  2. func run()
  3. }
  4. class Person { }
  5. class Student: Person, Runnable {
  6. func run() {
  7. print("Student run")
  8. }
  9. func study() {
  10. print("Student study")
  11. }
  12. }
  13. var stu: Any = 10
  14. print(stu is Int) // true
  15. stu = "Jack"
  16. print(stu is String) // true
  17. stu = Student()
  18. print(stu is Person) // true
  19. print(stu is Student) // true
  20. print(stu is Runnable) // true

as用来做强制类型转换

  1. protocol Runnable {
  2. func run()
  3. }
  4. class Person { }
  5. class Student: Person, Runnable {
  6. func run() {
  7. print("Student run")
  8. }
  9. func study() {
  10. print("Student study")
  11. }
  12. }
  13. var stu: Any = 10
  14. (stu as? Student)?.study() // 没有调用study
  15. stu = Student()
  16. (stu as? Student)?.study() // Student study
  17. (stu as! Student).study() // Student study
  18. (stu as? Runnable)?.run() // Student run
  1. var data = [Any]()
  2. data.append(Int("123") as Any)
  3. var d = 10 as Double
  4. print(d) // 10.0

元类型

X.self

X.self是一个元类型的指针,metadata存放着类型相关信息

X.self属于X.Type类型

  1. class Person { }
  2. class Student: Person { }
  3. var perType: Person.Type = Person.self
  4. var stuType: Student.Type = Student.self
  5. perType = Student.self
  6. var anyType: AnyObject.Type = Person.self
  7. anyType = Student.self
  8. var per = Person()
  9. perType = type(of: per)
  10. print(Person.self == type(of: per)) // true

AnyClass的本质就是AnyObject.Type

-w492

  1. var anyType2: AnyClass = Person.self
  2. anyType2 = Student.self

元类型的应用

  1. class Animal {
  2. required init() {
  3. }
  4. }
  5. class Cat: Animal {
  6. }
  7. class Dog: Animal {
  8. }
  9. class Pig: Animal {
  10. }
  11. func create(_ clses: [Animal.Type]) -> [Animal] {
  12. var arr = [Animal]()
  13. for cls in clses {
  14. arr.append(cls.init())
  15. }
  16. return arr
  17. }
  18. print(create([Cat.self, Dog.self, Pig.self]))
  19. // a1、a2、a3、a4的写法等价
  20. var a1 = Animal()
  21. var t = Animal.self
  22. var a2 = t.init()
  23. var a3 = Animal.self.init()
  24. var a4 = Animal.self()

Self

Self代表当前类型

  1. class Person {
  2. var age = 1
  3. static var count = 2
  4. func run() {
  5. print(self.age)
  6. print(Self.count)
  7. }
  8. }

Self一般用作返回值类型,限定返回值和方法调用者必须是同一类型(也可以作为参数类型)

  1. protocol Runnable {
  2. func test() -> Self
  3. }
  4. class Person: Runnable {
  5. required init() {
  6. }
  7. func test() -> Self {
  8. type(of: self).init()
  9. }
  10. }
  11. class Student: Person {
  12. }
  13. var p = Person()
  14. print(p.test()) // test_enum.Person
  15. var stu = Student()
  16. print(stu.test()) // test_enum.Student

元类型的本质

我们可以通过反汇编来查看元类型的实现是怎样的

  1. var p = Person()
  2. var pType = Person.self

我们发现最后存储到全局变量pType中的地址值就是一开始调用的地址

-w1031

再通过打印,我们发现pType的值就是Person实例对象的前8个字节的地址值,也就是类信息

-w1031
-w1032

我们再来看下面的示例代码

  1. var p = Person()
  2. var pType = type(of: p)

通过分析我们可以看到type(of: p)本质不是函数调用,只是将Person实例对象的前8个字节存储到pType中,也证明了元类型的本质就是存储的类信息

-w1031
-w1030

我们还可以用以下方式来获取Swift的隐藏基类_TtCs12_SwiftObject

  1. class Person {
  2. var age: Int = 0
  3. }
  4. class Student: Person {
  5. var no: Int = 0
  6. }
  7. print(class_getInstanceSize(Student.self)) // 32
  8. print(class_getSuperclass(Student.self)!) // Person
  9. print(class_getSuperclass(Student.self)!) // _TtCs12_SwiftObject
  10. print(class_getSuperclass(NSObject.self)) // nil

我们可以查看Swift源码来分析该类型

发现SwiftObject里面也有一个isa指针

-w686

原文链接:http://www.cnblogs.com/funkyRay/p/swift-jin-jie-jiu-xie-yi-yuan-lei-xing.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号