-
31. X.self、X.Type、AnyClass
- X.self(對應(yīng)OC中的類對象)是一個(gè)元類型(metadata)的指針,metadata存放著類型相關(guān)信息
- X.self屬于X.Type類型(對應(yīng)OC中的元類對象)
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
//元類型的應(yīng)用
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]))
- Swift還有個(gè)隱藏的基類:Swift._SwiftObject 源碼連接
-
32.大寫Self
Self
// Self代表當(dāng)前類型
class Person {
var age = 1
static var count = 2
func run() {
print(self.age) // 1
print(Self.count) // 2 等價(jià)于Person.count
}
}
// Self一般用作返回值類型,限定返回值跟方法調(diào)用者必須是同一類型(也可以作為參數(shù)類型)
protocol Runnable {
func test() -> Self
}
class Person : Runnable {
required init() {}
func test() -> Self {
type(of: self).init()
}
}
class Student : Person {}
var p = Person()
// Person
print(p.test())
var stu = Student()
// Student
print(stu.test())
-
33.錯誤處理
- 自定義錯誤:
// Swift中可以通過Error協(xié)議自定義運(yùn)行時(shí)的錯誤信息
enum SomeError : Error {
case illegalArg(String)
case outOfBounds(Int, Int)
case outOfMemory
}
// 函數(shù)內(nèi)部通過throw拋出自定義Error,可能會拋出Error的函數(shù)必須加上throws聲明
func divide(_ num1: Int, _ num2: Int) throws -> Int {
if num2 == 0 {
throw SomeError.illegalArg("0不能作為除數(shù)")
}
return num1 / num2
}
// 需要使用try調(diào)用可能會拋出Error的函數(shù)
var result = try divide(20, 10)
- 處理Error的3種方式:
1 通過do-catch捕捉Error
2 不捕捉Error,在當(dāng)前函數(shù)增加throws聲明,Error將自動拋給上層函數(shù), 如果最頂層函數(shù)(main函數(shù))依然沒有捕捉Error,那么程序?qū)⒔K止
3 可以使用try?、try!
調(diào)用可能會拋出Error的函數(shù),這樣就不用去處理Error
拋出Error后,try下一句直到作用域結(jié)束的代碼都將停止運(yùn)行
rethrows
rethrows表明:函數(shù)本身不會拋出錯誤,但調(diào)用閉包參數(shù)拋出錯誤,那么它會將錯誤向上拋
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)
defer
- defer語句:用來定義以任何方式(拋錯誤、return等)離開代碼塊前必須要執(zhí)行的代碼
- defer語句將延遲至當(dāng)前作用域結(jié)束之前執(zhí)行
// defer語句的執(zhí)行順序與定義順序相反
func test() {
defer { fn1() }
defer { fn2() }
}
test()
// fn2
// fn1
-
assert
(斷言)
- 很多編程語言都有斷言機(jī)制:不符合指定條件就拋出運(yùn)行時(shí)錯誤,常用于調(diào)試(Debug)階段的條件判斷
- 默認(rèn)情況下,Swift的斷言只會在Debug模式下生效,Release模式下會忽略
增加Swift Flags修改斷言的默認(rèn)行為
p-assert-config Release:強(qiáng)制關(guān)閉斷言
p-assert-config Debug:強(qiáng)制開啟斷言
fatalError
- 如果遇到嚴(yán)重問題,希望結(jié)束程序運(yùn)行時(shí),可以直接使用fatalError函數(shù)拋出錯誤(
這是無法通過do-catch捕捉的錯誤
) - 使用了fatalError函數(shù),就不需要再寫return
- 在某些不得不實(shí)現(xiàn)、但不希望別人調(diào)用的方法,可以考慮內(nèi)部使用fatalError函數(shù)
-
34.泛型
- 泛型可以將類型參數(shù)化,提高代碼復(fù)用率,減少代碼量
// 泛型函數(shù)賦值給變量 func swapValues<T>(_ a: inout T, _ b: inout T) { (a, b) = (b, a) }
- 關(guān)聯(lián)類型(Associated Type)
關(guān)聯(lián)類型的作用:給協(xié)議中用到的類型定義一個(gè)占位名稱。就是協(xié)議中泛型的實(shí)現(xiàn)
協(xié)議中可以擁有多個(gè)關(guān)聯(lián)類型
protocol Stackable { associatedtype Element // 關(guān)聯(lián)類型 mutating func push(_ element: Element) mutating func pop() -> Element func top() -> Element func size() -> Int } class StringStack : Stackable { // 給關(guān)聯(lián)類型設(shè)定真實(shí)類型 // 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")
-
where
類型約束
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 }
- 協(xié)議類型的注意點(diǎn)
-
some
不透明類型應(yīng)用
泛型解決 // 解決方案1:使用泛型 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) 不透明類型(Opaque Type): 屏蔽內(nèi)部實(shí)現(xiàn)細(xì)節(jié) // 解決方案2:使用some關(guān)鍵字聲明一個(gè)不透明類型 // some限制只能返回一種類型 func get(_ type: Int) -> some Runnable { Car() } var r1 = get(0) var r2 = get(1) // some // some除了用在返回值類型上,一般還可以用在屬性類型上 protocol Runnable { associatedtype Speed } class Dog : Runnable { typealias Speed = Double } class Person { var pet: some Runnable { return Dog() } }
- 可選項(xiàng)的本質(zhì)是
enum
類型
public enum Optional<Wrapped> : ExpressibleByNilLiteral { case none // nil case some(Wrapped) // 關(guān)聯(lián)值 public init(_ some: Wrapped) } var age: Int? = .none age = 10 age = .some(20) age = nil var age: Int? = 10 var age0: Optional<Int> = Optional<Int>.some(10) var age1: Optional = .some(10) var age2 = Optional.some(10) var age3 = Optional(10) age = nil age3 = .none // 兩種判斷nil的寫法 switch age { // 默認(rèn)會解包 case let v?: print("some", v) case nil: print("none") } switch age { case let .some(v): print("some", v) case .none: print("none") }
-
35. 關(guān)于String的本質(zhì)探索
// 字符串長度 <= 0xF(15),字符串內(nèi)容直接存放在str1變量的內(nèi)存中。(類似OC中的tagger pointer 技術(shù))
var str1 = "0123456789"
// 字符串長度 > 0xF,字符串內(nèi)容存放在__TEXT.cstring中(常量區(qū))
// 字符串的地址值信息存放在str2變量的后8個(gè)字節(jié)中
var str2 = "0123456789ABCDEF"
// 由于字符串長度 <= 0xF,所以字符串內(nèi)容依然存放在str1變量的內(nèi)存中
str1.append("ABCDE")
// 開辟堆空間
str1.append("F")
// 開辟堆空間
str2.append("G")
-
36.call,jmp指令區(qū)別
call會把他的下一條指令的地址壓入堆棧,然后跳轉(zhuǎn)到他調(diào)用的開始處,同時(shí)ret會自動彈出返回地址。
JMP只是簡單的跳轉(zhuǎn)
call的本質(zhì)相當(dāng)于push+jmp ret的本質(zhì)相當(dāng)于pop+jmp-
37.高級運(yùn)算符
- 溢出運(yùn)算符(Overflow Operator):
- Swift的算數(shù)運(yùn)算符出現(xiàn)溢出時(shí)會拋出運(yùn)行時(shí)錯誤
- Swift有溢出運(yùn)算符
(&+、&-、&*)
,用來支持溢出運(yùn)算var min = UInt8.min print(min &- 1) // 255, Int8.max var max = UInt8.max print(max &+ 1) // 0, Int8.min print(max &* 2) // 254, 等價(jià)于 max &+ max // 逐漸遞增 UInt8.min 0 UInt8.max 255
- 運(yùn)算符重載(Operator Overload):
- 類、結(jié)構(gòu)體、枚舉可以為現(xiàn)有的運(yùn)算符提供自定義的實(shí)現(xiàn),這個(gè)操作叫做:運(yùn)算符重載
struct Point : Equatable { var x = 0, y = 0 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 } }
- Equatable:
- 要想得知2個(gè)實(shí)例是否等價(jià),一般做法是遵守Equatable 協(xié)議,重載== 運(yùn)算符
- 與此同時(shí),等價(jià)于重載了 != 運(yùn)算符
- Swift為以下類型提供默認(rèn)的Equatable 實(shí)現(xiàn)
- 沒有關(guān)聯(lián)類型的枚舉
- 只擁有遵守 Equatable 協(xié)議關(guān)聯(lián)類型的枚舉
- 只擁有遵守 Equatable 協(xié)議存儲屬性的結(jié)構(gòu)體
- 引用類型比較存儲的地址值是否相等(是否引用著同一個(gè)對象),使用恒等運(yùn)算符=== 、!==
- Comparable
- 要想比較2個(gè)實(shí)例的大小,一般做法是: 1.遵守 Comparable 協(xié)議 2.重載相應(yīng)的運(yùn)算符
// 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
- 自定義運(yùn)算符(Custom Operator)
- 可以自定義新的運(yùn)算符:在全局作用域使用operator進(jìn)行聲明
Apple文檔參考1prefix operator 前綴運(yùn)算符 postfix operator 后綴運(yùn)算符 infix operator 中綴運(yùn)算符 : 優(yōu)先級組 precedencegroup 優(yōu)先級組 { associativity: 結(jié)合性(left\right\none) higherThan: 比誰的優(yōu)先級高 lowerThan: 比誰的優(yōu)先級低 assignment: true代表在可選鏈操作中擁有跟賦值運(yùn)算符一樣的優(yōu)先級 } prefix operator +++ infix operator +- : PlusMinusPrecedence precedencegroup PlusMinusPrecedence { associativity: none higherThan: AdditionPrecedence lowerThan: MultiplicationPrecedence assignment: true }
Apple文檔參考2- 示例:
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)
- 運(yùn)算符重載(Operator Overload):
-
38. 擴(kuò)展(Extension)
Swift中的擴(kuò)展,有點(diǎn)類似于OC中的分類(Category)
擴(kuò)展可以為枚舉、結(jié)構(gòu)體、類、協(xié)議添加新功能
可以添加方法、計(jì)算屬性、下標(biāo)、(便捷)初始化器、嵌套類型、協(xié)議等等
擴(kuò)展不能辦到的事情:
不能覆蓋原有的功能
不能添加存儲屬性,不能向已有的屬性添加屬性觀察器
不能添加父類
不能添加指定初始化器,不能添加反初始化器
...- 協(xié)議拓展
擴(kuò)展可以給協(xié)議提供默認(rèn)實(shí)現(xiàn),也間接實(shí)現(xiàn)『可選協(xié)議』的效果
擴(kuò)展可以給協(xié)議擴(kuò)充『協(xié)議中從未聲明過的方法』 - 注意下面代碼的打印信息:
protocol TestProtocol { func test1() } extension TestProtocol { func test1() { print("TestProtocol test1") } func test2() { print("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
- 泛型拓展:
// 符合條件才擴(kuò)展(只有Stack中的元素遵守Equatable協(xié)議,拓展才有效) extension Stack : Equatable where E : Equatable { static func == (left: Stack, right: Stack) -> Bool { left.elements == right.elements } }
-
39.訪問控制(Access Control)
- 在訪問權(quán)限控制這塊,Swift提供了5個(gè)不同的訪問級別(以下是從高到低排列, 實(shí)體指被訪問級別修飾的內(nèi)容)
open:允許在定義實(shí)體的模塊、其他模塊中訪問,允許其他模塊進(jìn)行繼承、重寫(open只能用在類、類成員上)
public:允許在定義實(shí)體的模塊、其他模塊中訪問,不允許其他模塊進(jìn)行繼承、重寫
internal:只允許在定義實(shí)體的模塊中訪問,不允許在其他模塊中訪問
fileprivate:只允許在定義實(shí)體的源文件中訪問
private:只允許在定義實(shí)體的封閉聲明中訪問
絕大部分實(shí)體默認(rèn)都是internal 級別
- 訪問級別的使用準(zhǔn)則:
一個(gè)實(shí)體不可以被更低訪問級別的實(shí)體定義
1.元祖,泛型都以最低那個(gè)為準(zhǔn)
2.協(xié)議實(shí)現(xiàn)不得小于協(xié)議和類中的較小的那個(gè)
3.枚舉不能每項(xiàng)單獨(dú)定義
4.public 限定作用域內(nèi)部默認(rèn)是internal
5.required初始化器 ≥ 它的默認(rèn)訪問級別
直接在全局作用域下定義的private等價(jià)于fileprivate
-
40.內(nèi)存管理
- 跟OC一樣,Swift也是采取基于引用計(jì)數(shù)的ARC內(nèi)存管理方案(針對堆空間)
- Swift的ARC中有3種引用:
1.強(qiáng)引用(
strong reference
):默認(rèn)情況下,引用都是強(qiáng)引用-
2.弱引用(
weak reference
):通過weak定義弱引用- 必須是可選類型的var,因?yàn)閷?shí)例銷毀后,ARC會自動將弱引用設(shè)置為nil
- ARC自動給弱引用設(shè)置nil時(shí),不會觸發(fā)屬性觀察器
-
3.無主引用(
unowned reference
):通過unowned定義無主引用- 不會產(chǎn)生強(qiáng)引用,實(shí)例銷毀后仍然存儲著實(shí)例的內(nèi)存地址(類似于OC中的unsafe_unretained)
- 試圖在實(shí)例銷毀后訪問無主引用,會產(chǎn)生運(yùn)行時(shí)錯誤(野指針)
? Fatal error: Attempted to read an unowned reference but object 0x0 was already deallocated
用
inout
輸入輸出參數(shù)時(shí),注意內(nèi)存訪問沖突問題內(nèi)存訪問沖突(Conflicting Access to Memory)
內(nèi)存訪問沖突會在兩個(gè)訪問滿足下列條件時(shí)發(fā)生:
至少一個(gè)是寫入操作
它們訪問的是同一塊內(nèi)存
它們的訪問時(shí)間重疊(比如在同一個(gè)函數(shù)內(nèi))
// 不存在內(nèi)存訪問沖突
func plus(_ num: inout Int) -> Int { num + 1 } var number = 1
number = plus(&number)
// 存在內(nèi)存訪問沖突
// Simultaneous accesses to 0x0, but modification requires exclusive access var step = 1
func increment(_ num: inout Int) { num += step }
increment(&step)
// 解決內(nèi)存訪問沖突
var copyOfStep = step
increment(©OfStep)
step = copyOfStep
以上均根據(jù)CoderMJLee老師的視頻及資料整理,祝小碼哥教育越來越好,特此感謝!!!
如有侵權(quán),請聯(lián)系刪除:fm939071955@163.com