站在汇编角度深入了解 Swift(十一)

2020/4/6 23:31:48

本文主要是介绍站在汇编角度深入了解 Swift(十一),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

可选链(Optional Channing)

  • 如果可选项为 nil,调用方法、下标、属性失败,结果为 nil
  • 如果可选项不为 nil,调用方法、下标、属性成功,结果会被包装成可选项
    • 如果结果本来就是可选项,不会进行再次包装

协议

  • 协议中定义属性时必须用 var 关键字
    • 因为你有可能是用计算属性实现的,那么这个值就不确定了,所以不能使用 let 关键字
  • 实现协议时的属性权限要不小于协议中定义的属性权限
    • 协议定义 { get set },用 var 存储属性或 get、set计算属性去实现
    • 协议定义 get,用任何属性都可以实现
  • 为了保证通用,协议中必须用 static 定义类型方法、类型属性,类型下标

mutating

  • 只有将协议中的实例方法标记为 mutating
    • 才允许结构体、枚举的具体实现修改自身内存
    • 类在实现方法时不用加 mutating,枚举、结构体才需要加 mutating

init

  • 协议中还可以定义初始化器 init
  • 非 final 类实现时必须加上 required
  • 如果协议实现的初始化器,刚好是重写了父类的指定初始化器
    • 那么这个初始化器必须同时加 required、override
  • init、init?、init!
    • 协议中定义的 init?、init!,可以用 init、init?、init! 去实现
    • 协议中定义的 init,可以用 init、init! 去实现
protocol Drawable {
    func draw()
    var x: Int { get set }
    var y: Int { get }
    subscript(index: Int) -> Int { get }
}

class Person: Drawable {
    var x: Int = 0
    
    let y: Int = 10
    
    func draw() {
        print("Person draw")
    }
    
    subscript(index: Int) -> Int {
        return index
    }
}
复制代码

协议组合

  • 可以包含很多个协议,可以包含一个类类型(最多一个)
protocol P1 {
    
}
protocol P2 {
    
}
class Person { }
func fn0(obj: P1 & P2 & Person) { }
复制代码

CaseIterable

  • 让枚举遵守 CaseIterable 就可以遍历枚举
enum Season {
    case spring, summer, autumn, winter
}
extension Season: CaseIterable { }
func test34() {
    let seasons = Season.allCases
    print(seasons)
}
复制代码

CustomStringConvertible

  • 遵守 CustomStringConvertible 协议,可以自定义实例的打印字符串
    • 和 OC 中的 description一样

Any、AnyObject

  • Swift 提供了2种特殊的类型
    • Any:可以代表任意类型(枚举、结构体、类,也包含函数类型)
    • AnyObject:可以代表任意类类型(在协议后面写上: AnyObject 代表只有类能遵守这个协议),在后面加 class 关键字也有这个作用

is、as?、as!、as

  • is 用来判断是否为某种类型,as用来强制类型转换
  • as? 表示可能转换失败, as! 表示对 as? 的结果进行解包
(stu as? Student)?.study() // 转化有可能会失败,失败的时候方法不调用
(stu as? Student)!.study() // 转化有可能会失败,但也强制解包,所以有可能发生闪退
(stu as! Student).study() // 和上面的完全等价,可以看成是上面这种写的简写
复制代码
as 在编译器层面就能确保你转换是一定能够成功的了
var d = 10 as Double
复制代码

X.self、X.Type、AnyClass

  • X.self 是一个元类型(metadata)指针,metadata 存放着类型相关信息。
    • 也就是堆内存的首8个字节
  • X.self 属于 X.Type 类型
  • public typealias AnyClass = AnyObject.Type
class Person { }
class Student: Person { }

let person = Person()
var perType: Person.Type = Person.self
let stuType: Student.Type = Student.self
perType = Student.self

var anyType: AnyObject.Type = Person.self
anyType = Student.self
// public typealias AnyClass = AnyObject.Type
var anyType2: AnyClass = Person.self
anyType2 = Student.self

---------------------汇编分析---------------------
register read rax
     rax = 0x0000000100014e48  type metadata for Person #1 in swiftstudy.test36() -> ()
(lldb) register read rax
     rax = 0x0000000102900000
(lldb) x/5wg 0x0000000102900000
0x102900000: 0x0000000100014e48 0x0000000000000002
0x102900010: 0x0000000000000000 0x0000000000000000
0x102900020: 0x2f6d657473790017
复制代码

从上面的汇编也验证了,其实X.self就是一个元类型(metadata)指针

元类型的使用

class Person {
    var age: Int = 0
}

class Student: Person {
    var no: Int = 0
}

print(class_getInstanceSize(Person.self))
print(class_getSuperclass(Student.self)!)
print(class_getSuperclass(Person.self)!)

---------------------汇编分析---------------------
24
Person
_TtCs12_SwiftObject
复制代码

可以看出来, Swift 还有个隐藏的基类:Swift.__SwiftObject,其实他和 oc 一样,前8个字节存的也是 isa 指针(就是用于查找他的类信息的),其实也可以理解,打印所有的纯 swift 对象,可以发现一个规律,就是前8个字节存储 isa,后面跟着8个字节存储引用计数,把他们抽成一个父类其实更合理。

Self

  • Self 一般用作返回值类型,限定返回值方法调用者必须是同一类型(也可以作为参数类型)
  • 如果 Self 用在类中,要求返回时调用的初始化是 required 的
  • 一般如果我想链式调用的话,会这样写
class Person {
    required init() {
        
    }
    
    func test() -> Self {
        Self.self.init()
    }
    
    func test1() -> Self {
        Self.self.init()
    }
}

class Student: Person {
    
}

let p = Person().test().test1()
复制代码

思考

  1. type(of: p) == Person.self? type(of: p) 和 sizeof 这种是一样的,本质不是调用函数,只是将 p 的前8个字节取出来,所以 type(of: p)和 Person.self 是相等的

  2. Person 和 Person.self 的异同

class Person {
    static var age = 0
    static func run() {}
}

Person.age = 10
Person.self.age = 20

func test(_ cls: AnyClass) {
    
}

test(Person.self)
复制代码

也就是说当你明确需要传 X.Type 的时候,这个时候X.self,不然用类名一般就够用了



这篇关于站在汇编角度深入了解 Swift(十一)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程