1、协议
1、协议可以用来定义方法、属性、下标的声明,协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)。
protocol Drawable {
func draw()
var x: Int { get set }
var y: Int { get }
subscript(index: Int) -> Int { get set }
}
protocol Test1 {}
protocol Test2 {}
protocol Test3 {}
class TestClass : Test1, Test2, Test3 {}
2、协议中定义方法时不能有默认参数值。默认情况下,协议中定义的内容必须全部都实现。
1、属性
协议中定义属性时必须用 var 关键字。实现协议时的属性权限要不小于协议中定义的属性权限:协议定义 get、set,用 var 存储属性或 get、set 计算属性去实现;协议定义 get,用任何属性都可以实现。
protocol Drawable {
func draw()
var x: Int { get set }
var y: Int { get }
subscript(index: Int) -> Int { get set }
}
class Person : Drawable {
var x: Int = 0
let y: Int = 0
func draw() {
print("Person draw")
}
subscript(index: Int) -> Int {
set {}
get { index }
}
}
class Person : Drawable {
var x: Int {
get { 0 }
set {}
}
var y: Int { 0 }
func draw() { print("Person draw") }
subscript(index: Int) -> Int {
set {}
get { index }
}
}
2、static、class
为了保证通用,协议中必须用 static 定义类型方法、类型属性、类型下标。这是因为用 class 的话只能被类使用,不能被值类型使用。
protocol Drawable {
static func draw()
}
class Person1 : Drawable {
class func draw() {
print("Person1 draw")
}
}
class Person2 : Drawable {
static func draw() {
print("Person2 draw")
}
}
3、mutating
只有将协议中的实例方法
标记为 mutating,才允许结构体、枚举的具体实现修改自身内存。类
在实现方法时不用加 mutating,枚举
、结构体
才需要加 mutating 。
protocol Drawable {
mutating func draw()
}
class Size : Drawable {
var width: Int = 0
func draw() {
width = 10
}
}
struct Point : Drawable {
var x: Int = 0
mutating func draw() {
x = 10
}
}
4、init、init?、init!
1、协议中还可以定义初始化器 init,非 final 类实现时必须加上 required。
protocol Drawable {
init(x: Int, y: Int)
}
class Point : Drawable {
required init(x: Int, y: Int) {}
}
final class Size : Drawable {
init(x: Int, y: Int) {}
}
2、如果从协议实现的初始化器,刚好是重写了父类的指定初始化器,那么这个初始化必须同时加 required、override。
protocol Livable {
init(age: Int)
}
class Person {
init(age: Int) {}
}
class Student : Person, Livable {
required override init(age: Int) {
super.init(age: age)
}
}
//也可以前面加 final 不用写 required
final class Student: Person, Livable {
override init(age: Int) {
super.init(age: age)
}
}
3、协议中定义的 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) {}
}
5、继承
一个协议可以继承其他协议。
protocol Runnable {
func run()
}
protocol Livable : Runnable {
func breath()
}
class Person : Livable {
func breath() {}
func run() {}
}
6、组合
协议组合,最多可以包含1个类类型
protocol Livable {}
protocol Runnable {}
class Person {}
// 接收Person或者其子类的实例
func fn0(obj: Person) {}
// 接收遵守Livable协议的实例
func fn1(obj: Livable) {}
// 接收同时遵守Livable、Runnable协议的实例
func fn2(obj: Livable & Runnable) {}
// 接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
func fn3(obj: Person & Livable & Runnable) {}
typealias RealPerson = Person & Livable & Runnable
// 接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
func fn4(obj: RealPerson) {}
7、CaseIterable
枚举遵守 CaseIterable 协议,可以实现遍历枚举值。
enum Season : CaseIterable {
case spring, summer, autumn, winter
}
let seasons = Season.allCases
print(seasons.count) // 4
for season in seasons {
print(season)
} // spring summer autumn winter
8、CustomStringConvertible
遵守 CustomStringConvertible、 CustomDebugStringConvertible 协议,都可以自定义实例的打印字符串。
class Person : CustomStringConvertible, CustomDebugStringConvertible {
var age = 0
var description: String { "person_\(age)" }
var debugDescription: String { "debug_person_\(age)" }
}
var person = Person()
print(person) // person_0
debugPrint(person) // debug_person_0
9、Any、AnyObject
Swift 提供了2种特殊的类型:Any、AnyObject。
Any:可以代表任意类型
(枚举、结构体、类,也包括函数类型)。
AnyObject:可以代表任意类类型
。在协议后面写上 AnyObject 代表只有类能遵守这个协议,在协议后面写上 class 也代表只有类能遵守这个协议。
var stu: Any = 10
stu = "Jack"
stu = Student()
// 创建1个能存放任意类型的数组
// var data = Array()
var data = [Any]()
data.append(1)
data.append(3.14)
data.append(Student())
data.append("Jack")
data.append({ 10 })
10、is、as?、as!、as
is 用来判断是否为某种类型,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
print(stu is Int) // true
stu = "Jack"
print(stu is String) // true
stu = Student()
print(stu is Person) // true
print(stu is Student) // true
print(stu is Runnable) // true
var stu: Any = 10
(stu as? Student)?.study() // 没有调用study
stu = Student()
(stu as? Student)?.study() // Student study
(stu as! Student).study() // Student study
(stu as? Runnable)?.run() // Student run
11、X.self、X.Type、AnyClass
X.self 是一个元类型(metadata)的指针,metadata 存放着类型相关信息。X.self 属于 X.Type 类型。
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
var per = Person()
var perType = type(of: per) // Person.self
print(Person.self == type(of: per)) // true
12、元类型
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]))
import Foundation
class Person {
var age: Int = 0
}
class Student : Person {
var no: Int = 0
}
print(class_getInstanceSize(Student.self)) // 32
print(class_getSuperclass(Student.self)!) // Person
print(class_getSuperclass(Person.self)!) // Swift._SwiftObject
13、Self
1、Self 代表当前类型。
class Person {
var age = 1
static var count = 2
func run() {
print(self.age) // 1
print(Self.count) // 2
}
}
2、Self 一般用作返回值类型,限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)。
protocol Runnable {
func test() -> Self
}
class Person : Runnable {
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
2、错误处理
开发过程常见的错误:语法错误(编译报错),逻辑错误,运行时错误(可能会导致闪退,一般也叫做异常)等。
1、自定义错误
1、Swift 中可以通过 Error 协议自定义运行时的错误信息。
enum SomeError : Error {
case illegalArg(String)
case outOfBounds(Int, Int)
case outOfMemory
}
2、函数内部通过 throw 抛出自定义 Error,可能会抛出 Error 的函数必须加上 throws 声明。
func divide(_ num1: Int, _ num2: Int) throws -> Int {
if num2 == 0 {
throw SomeError.illegalArg("0不能作为除数")
}
return num1 / num2
}
3、需要使用 try 调用可能会抛出 Error 的函数。
var result = try divide(20, 10)
2、do-catch
可以使用 do-catch 捕捉 Error。抛出 Error 后,try 下一句直到作用域结束的代码都将停止运行。
func test() {
print("1")
do {
print("2")
print(try divide(20, 0))
print("3")
} catch let SomeError.illegalArg(msg) {
print("参数异常:", msg)
} catch let SomeError.outOfBounds(size, index) {
print("下标越界:", "size=\(size)", "index=\(index)")
} catch SomeError.outOfMemory {
print("内存溢出")
} catch {
print("其他错误")
}
print("4")
}
test()
// 1
// 2
// 参数异常: 0不能作为除数
// 4
do {
try divide(20, 0)
} catch let error {
switch error {
case let SomeError.illegalArg(msg):
print("参数错误:", msg)
default:
print("其他错误")
}
}
3、处理 Error
处理 Error 的 2 种方式:
1、通过 do-catch 捕捉 Error。
2、不捕捉 Error,在当前函数增加 throws 声明,Error 将自动抛给上层函数。依次类推,如果最顶层函数(main函数)依然没有捕捉 Error,那么程序将终止。
do {
print(try divide(20, 0))
} catch is SomeError {
print("SomeError")
}
func test() throws {
print("1")
print(try divide(20, 0))
print("2")
}
try test()
// 1
// Fatal error: Error raised at top level
func test() throws {
print("1")
do {
print("2")
print(try divide(20, 0))
print("3")
} catch let error as SomeError {
print(error)
}
print("4")
}
try test()
// 1
// 2
// illegalArg("0不能作为除数")
// 4
4、try?、try!
可以使用 try?、try! 调用可能会抛出 Error 的函数,这样就不用去处理 Error。
func test() {
print("1")
var result1 = try? divide(20, 10) // Optional(2), Int?
var result2 = try? divide(20, 0) // nil
var result3 = try! divide(20, 10) // 2, Int
print("2")
}
test()
var a = try? divide(20, 0)
var b: Int?
do {
b = try divide(20, 0)
} catch { b = nil }
5、rethrows
rethrows 表明:函数本身不会抛出错误,但调用闭包参数抛出错误,那么它会将错误向上抛。
func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
print(try fn(num1, num2))
}
try exec(divide, 20, 0)// Fatal error: Error raised at top level
6、defer
defer 语句:用来定义以任何方式(抛错误、return等)离开代码块前必须要执行的代码。defer 语句将延迟至当前作用域结束之前执行。多个 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(20, 0)
// close将会在这里调用
}
try processFile("test.txt")// open -- > close --> Fatal error: Error raised at top level
7、assert(断言)
不符合指定条件就抛出运行时错误,常用于调试(Debug)阶段的条件判断。默认情况下,Swift 的断言只会在 Debug 模式下生效,Release 模式下会忽略。
func divide(_ v1: Int, _ v2: Int) -> Int {
assert(v2 != 0, "除数不能为0")
return v1 / v2
}
print(divide(20, 0))
8、fatalError
1、如果遇到严重问题,希望结束程序运行时,可以直接使用 fatalError 函数抛出错误(这是无法通过 do-catch 捕捉的错误)。使用了 fatalError 函数,就不需要再写 return。
func test(_ num: Int) -> Int {
if num >= 0 {
return 1
}
fatalError("num不能小于0")
}
2、在某些不得不实现、但不希望别人调用的方法,可以考虑内部使用 fatalError 函数。
class Person { required init() {} }
class Student : Person {
required init() { fatalError("don't call Student.init") }
init(score: Int) {}
}
var stu1 = Student(score: 98)
var stu2 = Student()
9、局部作用域
可以使用 do 实现局部作用域。
do {
let dog1 = Dog()
dog1.age = 10
dog1.run()
}
do {
let dog2 = Dog()
dog2.age = 10
dog2.run()
}
3、泛型
泛型可以将类型参数化,提高代码复用率,减少代码量。在使用的时候再确定具体类型。泛型可以使用在类、结构体、枚举。
func swapValues(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
var i1 = 10
var i2 = 20
swapValues(&i1, &i2)
var d1 = 10.0
var d2 = 20.0
swapValues(&d1, &d2)
struct Date {
var year = 0, month = 0, day = 0
}
var dd1 = Date(year: 2011, month: 9, day: 10)
var dd2 = Date(year: 2012, month: 10, day: 11)
swapValues(&dd1, &dd2)
func test(_ t1: T1, _ t2: T2) {}
var fn: (Int, Double) -> () = test
泛型使用
class Stack {
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 : Stack {}
struct Stack {
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 }
}
var stack = Stack()
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
enum Score {
case point(T)
case grade(String)
}
let score0 = Score.point(100)
let score1 = Score.point(99)
let score2 = Score.point(99.5)
let score3 = Score.grade("A")
1、关联类型
关联类型的作用:给协议中用到的类型定义一个占位名称。协议中可以拥有多个关联类型。
protocol Stackable {
associatedtype Element // 关联类型
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
class Stack : Stackable {
// typealias Element = 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 StringStack : Stackable {
// 给关联类型设定真实类型
// typealias Element = String
var elements = [String]()
func push(_ element: String) { elements.append(element) }
func pop() -> String { elements.removeLast() }
func top() -> String { elements.last! }
func size() -> Int { elements.count }
}
var ss = StringStack()
ss.push("Jack")
ss.push("Rose")
2、类型约束
protocol Runnable { }
class Person { }
func swapValues(_ a: inout T, _ b: inout T) {
(a, b) = (b, a)
}
protocol Stackable {
associatedtype Element: Equatable
}
class Stack : Stackable { typealias Element = E }
func equal(_ s1: S1, _ s2: S2) -> Bool where S1.Element == S2.Element, S1.Element : Hashable {
return false
}
var stack1 = Stack()
var stack2 = Stack()
// error: requires the types 'Int' and 'String' be equivalent
equal(stack1, stack2)
3、协议中使用 associatedtype 报错问题
protocol Runnable {
associatedtype Speed
var speed: Speed { get }
}
class Person: Runnable {
var speed: Double { 0.0 }
}
class Car: Runnable {
var speed: Int { 0 }
}
解决方案一:使用泛型
func get(_ type: Int) -> T {
if type == 0 {
return Person() as! T
}
return Car() as! T
}
var r1: Person = get(0)
var r2: Car = get(1)
解决方案二:使用 some 关键字声明一个不透明类型。some 限制只能返回一种类型。
func get(_ type: Int) -> some Runnable { Car() }
var r1 = get(0)
var r2 = get(1)
4、some
some 除了用在返回值类型上,一般还可以用在属性类型上。
protocol Runnable { associatedtype Speed }
class Dog : Runnable { typealias Speed = Double }
class Person {
var pet: some Runnable {
return Dog()
}
}
5、可选项的本质
可选项的本质是 enum 类型,要么为 none,要么 some。
public enum Optional : ExpressibleByNilLiteral {
case none
case some(Wrapped)
public init(_ some: Wrapped)
}
4、String 和 Array
1、1个 String 变量占用多少内存?
16 个字节
2、下面2个 String 变量,底层存储有什么不同?
var str1 = "0123456789"
var str2 = "0123456789ABCDEF"
str1 存储在 str1 的内存中,其中最高位用来标识字符串长度,其余位用来存储数据。
str2 存储在内存的常量区,str2 的前 8 个字节的最低位用来标识字符串的长度,后 8 个字节用来标识字符串存储的地址。
字符串的存储:先比较字符串的长度,如果字符串的长度小于 16,则字符串的存储类似 OC 中的 targetpoint 方式,直接存储在字符串的内存中;否则字符串存储在常量区。
3、如果对 String 进行拼接操作, String 变量的存储会发生什么变化?
1、若改变前字符串长度小于 16,改变后字符串长度扔小于 16,则存储在字符串的内存中。
2、过改变前字符串长度小于 16,改变后字符串长度大于 16,则开辟堆空间,存储在堆区。
3、若改变前字符串长度大于 16,改变后字符串长度扔大于 16,则开辟堆空间,存储在堆区。因为改变前字符串长度大于 16 是存储在常量区,常量区不能更改,故改变后的字符串存储在堆区。
4、若改变前字符串长度大于 16,改变后字符串长度小于于 16,则存储在字符串的内存中。
4、1个 Array 变量占用多少内存?
8 个字节
5、数组中的数据存放在哪里?
堆空间。
array 的 8 个字节里面存放的是堆空间的地址值。其第一个 8 字节存放着数组相关引用类型信息内存地址,第二个 8 字节存放引用计数,第三个 8 字节存放 array 元素数量,第四个 8 字节存放数组容量,后面存放数组内容。
注:
1、数组的容量会自动扩容至元素个数的两倍,且是8的倍数。
2、数组的表象是结构体,但其本质是引用类型。
5、高级运算符
1、溢出运算符
Swift 的算数运算符出现溢出时会抛出运行时错误。Swift 有溢出运算符(&+、&-、&*),用来支持溢出运算。
var min = UInt8.min
print(min &- 1) // 255, Int8.max
var max = UInt8.max
print(max &+ 1) // 0, Int8.min
print(max &* 2) // 254, 等价于 max &+ max
2、运算符重载
类、结构体、枚举可以为现有的运算符提供自定义的实现
,这个操作叫做:运算符重载。
struct Point {
var x: Int, y: Int
}
func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
print(p) // Point(x: 21, y: 42)
struct Point {
var x: Int, y: Int
static func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
}
static func + (p1: Point, p2: Point) -> Point {
Point(x: p1.x + p2.x, y: p1.y + p2.y)
}
static func - (p1: Point, p2: Point) -> Point {
Point(x: p1.x - p2.x, y: p1.y - p2.y)
}
static prefix func - (p: Point) -> Point {
Point(x: -p.x, y: -p.y)
}
static func += (p1: inout Point, p2: Point) {
p1 = p1 + p2
}
static prefix func ++ (p: inout Point) -> Point {
p += Point(x: 1, y: 1)
return p
}
static postfix func ++ (p: inout Point) -> Point {
let tmp = p
p += Point(x: 1, y: 1)
return tmp
}
static func == (p1: Point, p2: Point) -> Bool {
(p1.x == p2.x) && (p1.y == p2.y)
}
3、Equatable
1、要想得知2个实例是否等价,一般做法是遵守 Equatable 协议,重载 == 运算符,与此同时,等价于重载了 != 运算符。
struct Point : Equatable {
var x: Int, y: Int
}
var p1 = Point(x: 10, y: 20)
var p2 = Point(x: 11, y: 22)
print(p1 == p2) // false
print(p1 != p2) // true
2、Swift 为以下类型提供默认的 Equatable 实现:
没有关联类型的枚举。
只拥有遵守 Equatable 协议关联类型的枚举。
只拥有遵守 Equatable 协议存储属性的结构体。
3、引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符 === 、!==。
4、Comparable
要想比较2个实例的大小,一般做法是:遵守 Comparable 协议,重载相应的运算符。
// score大的比较大,若score相等,age小的比较大
struct Student : Comparable {
var age: Int
var score: Int
init(score: Int, age: Int) {
self.score = score
self.age = age
}
static func < (lhs: Student, rhs: Student) -> Bool {
(lhs.score < rhs.score)
|| (lhs.score == rhs.score && lhs.age > rhs.age)
}
static func > (lhs: Student, rhs: Student) -> Bool {
(lhs.score > rhs.score)
|| (lhs.score == rhs.score && lhs.age < rhs.age)
}
static func <= (lhs: Student, rhs: Student) -> Bool {
!(lhs > rhs)
}
static func >= (lhs: Student, rhs: Student) -> Bool {
!(lhs < rhs)
}
}
var stu1 = Student(score: 100, age: 20)
var stu2 = Student(score: 98, age: 18)
var stu3 = Student(score: 100, age: 20)
print(stu1 > stu2) // true
print(stu1 >= stu2) // true
print(stu1 >= stu3) // true
print(stu1 <= stu3) // true
print(stu2 < stu1) // true
print(stu2 <= stu1) // true
5、自定义运算符
可以自定义新的运算符:在全局作用域使用 operator 进行声明。
prefix operator 前缀运算符
postfix operator 后缀运算符
infix operator 中缀运算符 : 优先级组
precedencegroup 优先级组 {
associativity: 结合性(left\right\none)
higherThan: 比谁的优先级高
lowerThan: 比谁的优先级低
assignment: true代表在可选链操作中拥有跟赋值运算符一样的优先级
}
prefix operator +++
infix operator +- : PlusMinusPrecedence
precedencegroup PlusMinusPrecedence {
associativity: none
higherThan: AdditionPrecedence
lowerThan: MultiplicationPrecedence
assignment: true
}
struct Point {
var x: Int, y: Int
static prefix func +++ (point: inout Point) -> Point {
point = Point(x: point.x + point.x, y: point.y + point.y)
return point
}
static func +- (left: Point, right: Point) -> Point {
return Point(x: left.x + right.x, y: left.y - right.y)
}
static func +- (left: Point?, right: Point) -> Point {
print("+-")
return Point(x: left?.x ?? 0 + right.x, y: left?.y ?? 0 - right.y)
}
}
struct Person {
var point: Point
}
var person: Person? = nil
person?.point +- Point(x: 10, y: 20)
6、扩展
Swift 中的扩展,有点类似于 OC 中的分类(Category)。扩展可以为枚举、结构体、类、协议添加新功能,可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等。
1、计算属性
extension Double {
var km: Double { self * 1_000.0 }
var m: Double { self }
var dm: Double { self / 10.0 }
var cm: Double { self / 100.0 }
var mm: Double { self / 1_000.0 }
}
2、下标
extension Array {
subscript(nullable idx: Int) -> Element? {
if (startIndex..
3、方法
extension Int {
func repetitions(task: () -> Void) {
for _ in 0.. Int {
self = self * self
return self
}
}
4、嵌套
extension Int {
func repetitions(task: () -> Void) {
for _ in 0.. Int {
self = self * self
return self
}
enum Kind { case negative, zero, positive }
var kind: Kind {
switch self {
case 0: return .zero
case let x where x > 0: return .positive
default: return .negative
}
}
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..
5、初始化器
struct Point {
var x: Int = 0
var y: Int = 0
}
extension Point {
init(_ point: Point) {
self.init(x: point.x, y: point.y)
}
}
var p1 = Point()
var p2 = Point(x: 10)
var p3 = Point(y: 20)
var p4 = Point(x: 10, y: 20)
var p5 = Point(p4)
如果希望自定义初始化器的同时,编译器也能够生成默认初始化器,可以在扩展中编写自定义初始化器。required 初始化器也不能写在扩展中。
6、协议
class Person {
var age: Int
var name: String
init(age: Int, name: String) {
self.age = age
self.name = name
}
}
extension Person : Equatable {
static func == (left: Person, right: Person) -> Bool {
left.age == right.age && left.name == right.name
}
convenience init() {
self.init(age: 0, name: "")
}
}
如果一个类型已经实现了协议的所有要求,但是还没有声明它遵守了这个协议,可以通过扩展来让它遵守这个协议。
protocol TestProtocol {
func test()
}
class TestClass {
func test() {
print("test")
}
}
extension TestClass : TestProtocol {}
//编写一个函数,判断一个整数是否为奇数?
func isOdd(_ i: T) -> Bool {
i % 2 != 0
}
extension BinaryInteger {
func isOdd() -> Bool { self % 2 != 0 }
}
扩展可以给协议提供默认实现,也间接实现『可选协议』的效果。扩展可以给协议扩充『协议中从未声明过的方法』。
protocol TestProtocol {
func test1()
}
extension TestProtocol {
func test1() {
print("TestProtocol test1")
}
func test2() {
print("TestProtocol test2")
}
}
class TestClass : TestProtocol {}
var cls = TestClass()
cls.test1() // TestProtocol test1
cls.test2() // TestProtocol test2
var cls2: TestProtocol = TestClass()
cls2.test1() // TestProtocol test1
cls2.test2() // TestProtocol test2
class TestClass : TestProtocol {
func test1() { print("TestClass test1") }
func test2() { print("TestClass test2") }
}
var cls = TestClass()
cls.test1() // TestClass test1
cls.test2() // TestClass test2
var cls2: TestProtocol = TestClass()
cls2.test1() // TestClass test1
cls2.test2() // TestProtocol test2
7、泛型
class Stack {
var elements = [E]()
func push(_ element: E) {
elements.append(element)
}
func pop() -> E { elements.removeLast() }
func size() -> Int { elements.count }
}
// 扩展中依然可以使用原类型中的泛型类型
extension Stack {
func top() -> E { elements.last! }
}
// 符合条件才扩展
extension Stack : Equatable where E : Equatable {
static func == (left: Stack, right: Stack) -> Bool {
left.elements == right.elements
}
}
8、扩展不能办到的事情
1、不能覆盖原有的功能。
2、不能添加存储属性,不能向已有的属性添加属性观察器。
3、不能添加父类。
4、不能添加指定初始化器,不能添加反初始化器。