1.协议
- 协议可以用来定义方法,属性,下标的声明,协议可以被枚举,结构体,类遵守。
- 协议中定义属性时必须用
var
关键字 - 实现协议时的属性权限要不小于协议中定义的属性权限
- 协议定义
get
、set
,用var
存储属性或者get
、set
计算属性去实现 - 协议定义
get
,用任何属性都可以实现
- 协议定义
- 为了保证通用,协议中必须用
static
定义类型方法,类型属性,类型下标(class就只能用哪于类,因此用static更通用) - 只有将协议的实例方法标记为
mutating
才允许结构体,枚举的具体实现修改自身内存,类的方法实现时不用加mutating
,枚举、结构体才要加 - 协议中还可以定义初始化器
init
,非final
类实现时必须加上required
,可以保证子类继承也是遵守同一个初始化器的
protocol Drawable {
static func circle()
mutating func draw()
init(x: Int, y: Int)
var x: Int { get set }
var y: Int { get }
subscript(index: Int) -> Int { get set }
}
class Pencil : Drawable{
static func circle() {
print("Pencil Circle 静态方法")
}
required init(x: Int, y: Int) {
self.x = x
self.y = y
}
func draw() {
x = 99
print("Pencil Draw")
}
var x: Int = 100
var y: Int = 200
subscript(index: Int) -> Int {
set {x = newValue}
get { index }
}
}
var p1 = Pencil(x: 100, y: 200)
p1.draw()
Pencil.circle()
print(p1[100])
struct Brush : Drawable {
static func circle() {
print("Brush Circle 静态方法")
}
mutating func draw() {
x = 88
print("Brush Draw")
}
var x: Int = 10
var y: Int = 20
subscript(index: Int) -> Int {
set {x = newValue}
get { index }
}
}
- 如果从协议实现的初始化器,同时刚好重写了父类的指定初始化器,那么这个初始化器必须同时加
required
、override
protocol Livable {
init(age: Int)
}
class People {
init(age: Int) {}
}
class Student : People, Livable {
required override init(age: Int) {
super.init(age: age)
}
}
- 协议中定义了
init?
、init!
,可以用init?
、init!
、init
去实现 - 协议中定义了
init
,可以用init!
、init
去实现
protocol Livable {
init()
init?(age: Int)
init!(no: Int)
}
class Person : Livable {
required init() {}
// required init!() {}
required init?(age: Int) {}
// required init!(age: Int) {}
// required init(age: Int) {}
required init!(no: Int) {}
// required init?(no: Int) {}
// required init(no: Int) {}
}
2.Any、 AnyObject
Any
: 可以代表任意类型(枚举,结构体,类,函数类型)AnyObject
: 可以代表任意类
类型,写在协议后面,AnyObject
代表只有类能遵守这个协议
var stu: Any = 10
stu = "Jack"
stu = Student()
// var data = Array<Any>()
var data = [Any]()
data.append(1)
data.append(Student())
data.append("Jack")
data.append({10})
3.is、as?、as!、as
is
用来判断是否为某种类型
protocol Runnable {
func run()
}
class Person {}
class Student: Person, Runnable {
func run() {
print("Student Run")
}
func study() {
print("Student Study")
}
}
var stu: Any = 10
print(stu is Int) // true
stu = Student()
print(stu is Student) // true
print(stu is Person) // true
print(stu is Runnable)// true
// 类似OC里面的isKindOf
as
用来做类型强制转换
protocol Runnable {
func run()
}
class Person {}
class Student: Person, Runnable {
func run() {
print("Student Run")
}
func study() {
print("Student Study")
}
}
var stu: Any = 10
(stu as? Student)?.study() // 不会调用
stu = Student()
(stu as? Student)?.study() // Student study
(stu as! Student).study() // Student study
(stu as? Runnable)?.run() // Student Run
var data = Array<Any>()
data.append(Int("123") as Any)
print(data)
data.append(10 as Any)
print(data)
// [Optional(123)]
// [Optional(123), 10]
4.X.self、X.Type、AnyClass
X.self
是一个元类型(metadata)的指针,存放着类型相关信息X.self
是X.Type
类型
var p: Person = Person()
var pType: Person.Type = Person.self
PersonType
类型是什么东西?
查看汇编右侧有基本的提示
0x100001988 <+24>: callq 0x1000019c0 ; type metadata accessor for Swift02.Person at <compiler-generated>
0x10000198d <+29>: movq %rax, %r13
0x100001990 <+32>: movq %rax, -0x20(%rbp)
该函数调用返回值写入rax
,然后再写入-0x20(%rbp)
保存
(lldb) register read rax
rax = 0x0000000100002180 type metadata for Swift02.Person
接着看到Person
的初始化赋值给全局变量p
0x100001998 <+40>: callq 0x100001a10 ; Swift02.Person.__allocating_init() -> Swift02.Person at main.swift:156
0x10000199d <+45>: xorl %r8d, %r8d
0x1000019a0 <+48>: movq %rax, 0x8f9(%rip) ; Swift02.p : Swift02.Person
0x1000019a7 <+55>: movq -0x20(%rbp), %rax
可以通过register
的方式
(lldb) register read rax
rax = 0x000000010061cb50
或者0x8f9 + 0x1000019a7 = 0x1000022A0
,通过View Memory
的方式查看前八个字节,也是0x000000010061cb50
,打印出来查看
(lldb) x/4xg 0x000000010061cb50
0x10061cb50: 0x0000000100002180 0x0000000000000002
0x10061cb60: 0x55706f50534e5b2d 0x206e6f7474754270
最后的汇编是
0x1000019a7 <+55>: movq -0x20(%rbp), %rax
0x1000019ab <+59>: movq %rax, 0x8f6(%rip) ; Swift02.pType : Swift02.Person.Type
最终还是把存起来的-0x20(%rbp)
赋值给rax
,然后赋值给全局变量pType
,可以看到存储的地址值就是0x0000000100002180
,所以X.Type
也是指针类型,存储着类对象前八个字节,metadata
的信息
X.self
类似于 NSObject.class
4.1继承相关
class Person {}
class Student : Person {}
var perType: Person.Type = Person.self
var 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
按指针和继承理解,父类指针一样可以指向子类对象
type(of:)
等价于oc的isMemberOf
class Person {}
class Student : Person {}
var per = Person()
var stu = Student()
var perType = type(of: per) // Person.self
print(Person.self == type(of: per)) // true
print(Person.self == type(of: stu)) // false
通过汇编查看
var perType = type(of: per)
这句代码对应的就是红色框框圈起来的汇编
把rdx
赋值上去,一步步上上推,就是
movq 0xf3b(%rip), %rax ; Swift02.per : Swift02.Person
0x1000014e5 <+133>: leaq -0x20(%rbp), %rdi
意思是把 0xf3b + 0x1000014e5 = 0x100002420
地址存储的前八个字节,通过ViewMemory查看
B0 32 58 00 01 00 00 00
// 0x1005832B0 给到rax
赋值给rax
寄存器,然后赋值给-0x88(%rb)
,最后给rcx = 0x1005832B0
,继续看下去
movq (%rcx), %rdx
0x10000150a <+170>: movq %rdx, 0xf1f(%rip) ; Swift02.perType : Swift02.Person.Type
(%rxc)
通过0x1005832B0
查看
(lldb) x/xg 0x1005832B0
0x1005832b0: 0x0000000100002270
(lldb) image lookup --address 0x100002270
Address: Swift02[0x0000000100002270] (Swift02.__DATA.__data + 56)
Summary: type metadata for Swift02.Person
然后赋值到rdx
,在给到全局变量perType
的地址中存储
因此可以看到type(of: )
其实严格意义上不算函数,或者说被编译器内联展开了,反正看到的现象是直接取的对象全八个字节进行赋值而已。
5.元类型的使用
class Animal { required init() {} }
class Cat : Animal {}
class Dog : Animal {}
class Pig : Animal {}
func create(_ clses: [Animal.Type]) -> [Animal] {
var arr = [Animal]()
for cls in clses {
arr.append(cls.init())
}
return arr
}
print(create([Cat.self, Dog.self, Pig.self]))
这里可以看到元类型Animal
的初始化器加了required
,这个细节主要用来表明子类都必须用该初始化器来初始化,统一调用。
swift获取父类的信息
class Person {
var age: Int = 0
}
class Student: Person {
var no: Int = 0
}
print(class_getInstanceSize(Student.self)) // 32
print(class_getSuperclass(Student.self)!) // Persn=on
print(class_getSuperclass(Person.self)!) // Swift._SwiftObject
6.Self
类似OC的instancetype
Self
一般用作返回值类型,限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)
protocol Runable {
func test() -> Self
}
class Person: Runable {
required init() {}
func test() -> Self {
type(of: self).init()
}
}
class Student: Person {}
var p = Person()
// Person
p.test()
var stu = Student()
// Student
stu.test()
上面提到的很多加了require,主要服务于子类,抽象
7.错误处理
7.1throws & rethrows
enum SomeError : Error {
case illegalArgs(String)
case outOfBounds(Int, Int)
case outOfMemory
}
func divide(num1: Int, num2: Int) throws -> Int {
if num2 == 0 {
throw SomeError.illegalArgs("参数不能为0")
}
return num1 / num2
}
func test() {
print("1")
do {
print("2")
print(try divide(num1: 10, num2: 0))
print("3")
print("3")
print("3")
} catch let SomeError.illegalArgs(msg) {
print("参数异常 :", msg)
} catch let SomeError.outOfBounds(size, index){
print("下标越界:","size\(size)","index=\(index)")
} catch SomeError.outOfMemory{
print("内存溢出")
} catch {
print("其他错误")
}
print("4")
}
test()
func test1() {
do {
try divide(num1: 10, num2: 0)
} catch let error {
switch error {
case let SomeError.illegalArgs(msg):
print("参数错误:",msg)
default:
print("其他错误")
}
}
}
// 1
// 2
// 参数异常 : 参数不能为0
// 4
- 抛出
error
后,try
下一句直到作用域结束的代码都将停止运行 - 通过
do-catch
捕捉错误 - 不捕捉错误,通过在函数增加
throws
声明,Error将自动往上抛 - 如果是顶层函数,
main
函数依旧没有捕获error
,那么程序就会终止
rethrows
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
print(try fn(num1, num2))
}
// Fatal error: Error raised at top level
try exec(divide, 20, 0)
- 其实
rethrows
只是一个声明,代表函数本身不会抛出错误,但是调用内部闭包会抛出错误,那么它将会把错误继续往上抛
7.2 try? & try!
print(try? divide(num1: 10, num2: 0)) // nil
print(try? divide(num1: 10, num2: 2)) // Optional(5)
print(try! divide(num1: 10, num2: 2)) // 5
等价
var a = try? divide(num1: 10, num2: 2)
var b: Int?
do{
b = try divide(num1: 10, num2: 2)
} catch {
b = nil
}
可以把?
理解为可选链一样的意思,如果没有错误抛出,就调用,有错误就是nil
,因此返回值必然是Optional
类型的
7.3 defer
defer
用来定义任何方式(抛错误,return等),离开代码块之前必须要执行的代码defer
语句将延迟到当前作用于结束之前执行
func open(_ filename: String) -> Int {
print("open")
return 0
}
func close(_ file: Int) {
print("close")
}
func processFile(_ filename: String) throws {
let file = open(filename)
defer {
close(file)
}
// 使用file
// ....
try divide(num1: 20, num2: 0)
}
// close将会在这里调用 }
try processFile("test.txt")
// open
// close
// Fatal error: Error raised at top level
// defer语句的执行顺序与定义顺序相反
func fn1() { print("fn1") }
func fn2() { print("fn2") }
func test3() {
defer { fn1() }
defer { fn2() }
}
test3()
// fn2
// fn1
8. 泛型
泛型可以将类型参数化,提高代码复用率,减少代码量
func swapVaues<T>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
var fn: (inout Double, inout Double) -> () = swapVaues
var n1 = 10
var n2 = 20
swapVaues(&n1, &n2)
var d1 = 20.0
var d2 = 30.0
fn(&d1, &d2)
类,结构体,枚举的泛型
// class
class Stack<E> {
var elements = [E]()
func push(_ element: E) { elements.append(element) }
func pop() -> E { elements.removeLast() }
func top() -> E { elements.last! }
func size() -> Int { elements.count }
}
class SubStack<E> : Stack<E> {}
var stack = Stack<Int>()
stack.push(11)
stack.push(22)
stack.push(33)
print(stack.top()) // 33
print(stack.pop()) // 33
print(stack.pop()) // 22
print(stack.pop()) // 11
print(stack.size()) // 0
// struct
struct Stack_S<E> {
var elements = [E]()
mutating func push(_ element: E) { elements.append(element) }
mutating func pop() -> E { elements.removeLast() }
func top() -> E { elements.last! }
func size() -> Int { elements.count }
}
// enum
enum Score<T> {
case point(T)
case grade(String)
}
let score0 = Score<Int>.point(100)
let score1 = Score.point(99)
let score2 = Score.point(99.5)
let score3 = Score<Int>.grade("A")
8.1 关联类型
类,结构体,枚举的泛型是上面这样的,但是protocol协议
的泛型就不一样了,只能通过关联类型来处理,而且协议中支持多个关联类型
protocol Stackable {
// 协议中的泛型,使用 associatedtype 关联类型来标记
associatedtype Element
// associatedtype ElementB
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
class StringStack: Stackable {
typealias Element = String
var elements = Array<String>()
func push(_ element: String) {
elements.append(element)
}
func pop() -> String {
elements.removeLast()
}
func top() -> String {
elements.last ?? ""
}
func size() -> Int {
elements.count
}
}
var strs = StringStack()
strs.push("qishu")
strs.push("kejing")
class Stack<E>: Stackable {
typealias Element = E
var elements = Array<E>()
func push(_ element: E) {
elements.append(element)
}
func pop() -> E {
elements.removeLast()
}
func top() -> E {
elements.last!
}
func size() -> Int {
elements.count
}
}
var ss = Stack<Int>()
ss.push(122)
ss.push(100)
8.2 类型约束
protocol Runable {}
class Person { }
func swapValues<T : Person & Runable>(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
protocol Stackable {
associatedtype Element: Equatable
}
class Stack<E : Equatable>: Stackable {
typealias Element = E
}
func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool
where S1.Element == S2.Element, S1.Element: Hashable{
return false
}
var stack1 = Stack<Int>()
var stack2 = Stack<String>()
var stack3 = Stack<Int>()
// Global function 'equal' requires the types 'Int' and 'String' be equivalent
//equal(stack1, stack2)
print(equal(stack1, stack3))
8.3 协议泛型中的注意点,不透明类型(Opaque Type)
首先来看下返回遵守某个协议类型的函数
protocol Runable {}
class Person : Runable {}
class Car: Runable {}
func get(_ type: Int) -> Runable {
if type == 0 {
return Person()
}
return Car()
}
var r1 = get(0)
var r2 = get(1)
以上这种也是屏蔽了具体类型,开放Runable
接口给外部。下面看下泛型协议,具有关联类型的代码
protocol Runable {
associatedtype Speed
var speed: Speed { get }
}
class Person : Runable {
typealias Speed = Double
var speed: Double { 1.0 }
}
class Car: Runable {
typealias Speed = Int
var speed: Int { 2 }
}
func get(_ type: Int) -> Runable {
if type == 0 {
return Person()
}
return Car()
}
var r1 = get(0)
var r2 = get(1)
报错如下
Protocol 'Runable' can only be used as a generic constraint because it has Self or associated type requirements
这边可以理解为,Runable
泛型关联类型,get
方法返回了Runable
协议类型,但是由于实际类型有可可能是Car
或者是Person
,关联类型可能是Int
,Double
以及其他,总之返回的协议类型,没法确定关联类型的唯一性,因此报错。
解决方法:不透明类型
func get(_ type: Int) -> some Runable {
if type == 0 {
return Person()
}
return Car()
}
上面代码会报错
对于声明了some
关键字的不透明类型,限制只能返回一种类型,这样返回值就能去确认唯一的关联类型了。
func get(_ type: Int) -> some Runable {
return Car()
}
除了用在返回值上,还能在属性类型上
protocol Runable {
associatedtype Speed
}
class Dog: Runable {
typealias Speed = Double
}
class Person {
var pet: some Runable {
return Dog()
}
}
可以看到Runable
协议是泛型的,因此属性用到的地方,也需要申明some
,标记为不透明类型,而且只返回一种类型。