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()
// 創(chuàng)建1個能存放任意類型的數組
// var data = Array<Any>()
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离福、錯誤處理
開發(fā)過程常見的錯誤:語法錯誤(編譯報錯),邏輯錯誤炼蛤,運行時錯誤(可能會導致閃退妖爷,一般也叫做異常)等。
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等)離開代碼塊前必須要執(zhí)行的代碼。defer 語句將延遲至當前作用域結束之前執(zhí)行羽嫡。多個 defer 語句的執(zhí)行順序與定義順序相反本姥。
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<T>(_ 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, T2>(_ t1: T1, _ t2: T2) {}
var fn: (Int, Double) -> () = test
泛型使用
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> {}
struct Stack<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 }
}
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
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")
1矮燎、關聯類型
關聯類型的作用:給協議中用到的類型定義一個占位名稱。協議中可以擁有多個關聯類型赔癌。
protocol Stackable {
associatedtype Element // 關聯類型
mutating func push(_ element: Element)
mutating func pop() -> Element
func top() -> Element
func size() -> Int
}
class Stack<E> : 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<T : Person & Runnable>(_ 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>()
// 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<T : Runnable>(_ 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<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
public init(_ some: Wrapped)
}
4埠忘、String 和 Array
1、1個 String 變量占用多少內存馒索?
16 個字節(jié)
2莹妒、下面2個 String 變量,底層存儲有什么不同绰上?
var str1 = "0123456789"
var str2 = "0123456789ABCDEF"
str1 存儲在 str1 的內存中旨怠,其中最高位用來標識字符串長度,其余位用來存儲數據蜈块。
str2 存儲在內存的常量區(qū)鉴腻,str2 的前 8 個字節(jié)的最低位用來標識字符串的長度,后 8 個字節(jié)用來標識字符串存儲的地址百揭。
字符串的存儲:先比較字符串的長度爽哎,如果字符串的長度小于 16,則字符串的存儲類似 OC 中的 targetpoint 方式器一,直接存儲在字符串的內存中课锌;否則字符串存儲在常量區(qū)。
3、如果對 String 進行拼接操作渺贤, String 變量的存儲會發(fā)生什么變化雏胃?
1、若改變前字符串長度小于 16志鞍,改變后字符串長度扔小于 16瞭亮,則存儲在字符串的內存中。
2固棚、過改變前字符串長度小于 16统翩,改變后字符串長度大于 16,則開辟堆空間玻孟,存儲在堆區(qū)唆缴。
3、若改變前字符串長度大于 16黍翎,改變后字符串長度扔大于 16面徽,則開辟堆空間,存儲在堆區(qū)匣掸。因為改變前字符串長度大于 16 是存儲在常量區(qū)趟紊,常量區(qū)不能更改,故改變后的字符串存儲在堆區(qū)碰酝。
4霎匈、若改變前字符串長度大于 16,改變后字符串長度小于于 16送爸,則存儲在字符串的內存中铛嘱。
4、1個 Array 變量占用多少內存袭厂?
8 個字節(jié)
5墨吓、數組中的數據存放在哪里?
堆空間纹磺。
array 的 8 個字節(jié)里面存放的是堆空間的地址值帖烘。其第一個 8 字節(jié)存放著數組相關引用類型信息內存地址,第二個 8 字節(jié)存放引用計數橄杨,第三個 8 字節(jié)存放 array 元素數量秘症,第四個 8 字節(jié)存放數組容量,后面存放數組內容式矫。
注:
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 中綴運算符 : 優(yōu)先級組
precedencegroup 優(yōu)先級組 {
associativity: 結合性(left\right\none)
higherThan: 比誰的優(yōu)先級高
lowerThan: 比誰的優(yōu)先級低
assignment: true代表在可選鏈操作中擁有跟賦值運算符一樣的優(yōu)先級
}
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..<endIndex).contains(idx) {
return self[idx]
}
return nil
}
}
3由境、方法
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self { task() }
}
mutating func square() -> Int {
self = self * self
return self
}
}
4棚亩、嵌套
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self { task() }
}
mutating func square() -> 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..<digitIndex { decimalBase *= 10 }
return (self / decimalBase) % 10
}
}
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<T: BinaryInteger>(_ 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<E> {
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慷嗜、不能添加指定初始化器淀弹,不能添加反初始化器。