自定義
Description
內(nèi)容打印
- 通過(guò)遵守
CustomStringConvertible
、CustomDebugStringConvertible
協(xié)議來(lái)自定義實(shí)例打印字符串- 遵守
CustomStringConvertible
、CustomDebugStringConvertible
協(xié)議 - 實(shí)現(xiàn)協(xié)議中的
description: String
、debugDescription: String
類(lèi)型變量get
方法來(lái)確保內(nèi)容自定義
- 遵守
- 需要注意的點(diǎn)
-
print
調(diào)用的是CustomStringConveritable
協(xié)議的description
-
debugPrint
、po
調(diào)用的是CustomDebugStringConveritable
協(xié)議的debugDescription
-
關(guān)于
Self
的使用
- 特點(diǎn)
-
Self
代表當(dāng)前的類(lèi)型 -
Self
一般用作返回值類(lèi)型,限定返回值跟方法調(diào)用者必須是同一類(lèi)型(也可以作為參數(shù)類(lèi)型)。比如協(xié)議中定義一個(gè)方法,方法的返回值可以是Self
來(lái)確保實(shí)現(xiàn)協(xié)議的類(lèi)型描述
-
關(guān)于斷言
assert
- 特點(diǎn)
- 常用于調(diào)試階段,默認(rèn)debug模式生效,發(fā)布模式會(huì)忽略
- 通過(guò)在
other Swift flags
中設(shè)置Debug
模式下的-assert-coinfig Release
來(lái)強(qiáng)制關(guān)閉斷言 - 通過(guò)在
other Swift flags
中設(shè)置Release
模式下的-assert-coinfig debug
來(lái)強(qiáng)制關(guān)閉斷言
//條件滿(mǎn)足true的時(shí)候才會(huì)繼續(xù)執(zhí)行,否則中斷進(jìn)程,打印錯(cuò)誤
assert(fasle, "斷言錯(cuò)誤")
關(guān)于
fatalError
錯(cuò)誤
- 特點(diǎn)
-
fatalError
錯(cuò)誤是無(wú)法被do-catch
所捕獲的 - 使用了
fatalError
函數(shù),不需要再寫(xiě)return
返回值了 - 在某些不得不實(shí)現(xiàn)、但是又不希望別人調(diào)用的方法,可以考慮內(nèi)部使用
fatalError
函數(shù)
-
訪(fǎng)問(wèn)控制
-
Swift提供了五個(gè)不同層面的訪(fǎng)問(wèn)限制
-
open
: 允許其他模塊訪(fǎng)問(wèn),并允許其他模塊進(jìn)行繼承、重寫(xiě)(open
只能用在類(lèi)、類(lèi)成員上) -
public
: 允許在定義實(shí)體的模塊、其他模塊中訪(fǎng)問(wèn),不允許其他模塊進(jìn)行繼承、重寫(xiě) -
internal
: 只允許在定義實(shí)體的模塊訪(fǎng)問(wèn),不允許在其他模塊訪(fǎng)問(wèn) -
fileprivate
:只允許定義實(shí)體的源文件中訪(fǎng)問(wèn) -
private
:只允許定義實(shí)體的封閉聲明中訪(fǎng)問(wèn)
注: 絕大部分實(shí)體默認(rèn)都是
internal
級(jí)別 -
-
訪(fǎng)問(wèn)級(jí)別的使用準(zhǔn)則
- 一個(gè)實(shí)體不能被更低訪(fǎng)問(wèn)級(jí)別的實(shí)體定義
- 變量/常量類(lèi)型 >= 變量/常量
- 父類(lèi) >= 子類(lèi)
- 父協(xié)議 >= 子協(xié)議
- 原類(lèi)型 >= typealias
- 原始值類(lèi)型、關(guān)聯(lián)值類(lèi)型 >= 枚舉類(lèi)型
- 定義類(lèi)型A時(shí)用到的其他類(lèi)型 >= 類(lèi)型A
class Person{}
public typealias MuyPerson = Person //該語(yǔ)句會(huì)報(bào)錯(cuò),原因是public訪(fǎng)問(wèn)級(jí)別低于class Person 的默認(rèn)訪(fǎng)問(wèn)級(jí)別 internal
元組類(lèi)型
元組類(lèi)型的訪(fǎng)問(wèn)級(jí)別是所有成員類(lèi)型最低的那個(gè)泛型類(lèi)型
泛型類(lèi)型的訪(fǎng)問(wèn)級(jí)別是類(lèi)型的訪(fǎng)問(wèn)級(jí)別
以及所有泛型類(lèi)型參數(shù)的訪(fǎng)問(wèn)級(jí)別
中最低的那個(gè)-
成員嵌套類(lèi)型
- 類(lèi)型的訪(fǎng)問(wèn)級(jí)別會(huì)影響成員(屬性、方法、初始化器、下標(biāo))、嵌套類(lèi)型的默認(rèn)訪(fǎng)問(wèn)級(jí)別
- 一般情況下,類(lèi)型為
private
或者fileprivate
,那么成員/嵌套類(lèi)型默認(rèn)也是private
或者fileprivate
- 一般情況下,類(lèi)型為
internal
或者public
,那么成員/嵌套類(lèi)型默認(rèn)是internal
注:
- 父類(lèi)的訪(fǎng)問(wèn)權(quán)限要小于子類(lèi)
- 直接在全局作用域下定義的
private
等價(jià)于fileprivate
public class PublicClass {
public var p1 = 0
var p2 = 0 //internal
fileprivatre func f1() {} //fileprivate
private func f2() {} //private
}
class InternalClass { // internal
var p = 0 // internal
fileprivate func f1() {} //fileprivate
private func f2 () {} //private
}
class Test {
private class Person {} //private所作用的范圍是外面的大括號(hào)
fileprivate class Student : Person {} //會(huì)報(bào)錯(cuò),因?yàn)樽宇?lèi)Student的訪(fǎng)問(wèn)權(quán)限低于父類(lèi)
}
private class Person {} //private所作用的范圍是整個(gè)文件,由于文件聲明在全局區(qū)域,所以private作用域等同于fileprivate
fileprivate class Student : Person {} //不會(huì)報(bào)錯(cuò),此時(shí)兩者的權(quán)限是一致的
/*
報(bào)錯(cuò)原因:
1. Dog中的成員變量是由private修飾,所以在其所在的作用域內(nèi)是能夠訪(fǎng)問(wèn)的,由于Person中walk方法在Dog成員變量的作用域外,所以不能夠訪(fǎng)問(wèn)其成員變量以及方法
2. 由于 Dog 定義在全局區(qū),即使使用關(guān)鍵字 private 修飾,也等同于 fileprivate 修飾,所以 Person 類(lèi)與其成員變量 Dog 類(lèi)型訪(fǎng)問(wèn)權(quán)限一致,所以能夠正常實(shí)例
*/
private struct Dog {
private var age: Int = 0
private func run() {}
}
fileprivate struct Person {
var dog: Dog = Dog()
mutating func walk() {
dog.run() // 會(huì)報(bào)訪(fǎng)問(wèn)錯(cuò)誤
dog.age = 1 // 會(huì)報(bào)訪(fǎng)問(wèn)錯(cuò)誤
}
}
-
getter
、setter
訪(fǎng)問(wèn)權(quán)限- 兩者默認(rèn)自動(dòng)接收他們所屬環(huán)境的訪(fǎng)問(wèn)級(jí)別
- 可以給
setter
單獨(dú)設(shè)置一個(gè)比getter
更低的訪(fǎng)問(wèn)級(jí)別,用以限制寫(xiě)的權(quán)限
fileprivate(set) public var num = 10 class Person { private(set) var age = 0 fileprivate(set) public var weight: Int { set {} get { 10 } } internal(set) public subscript(index: Int) -> Int { set {} get { index } } }
-
公開(kāi)部分方法的訪(fǎng)問(wèn)權(quán)限可以通過(guò)
public
修飾如果想讓某個(gè)類(lèi)的方法能夠提供給其他模塊使用,可以通過(guò)
public
修飾方法,表示其可以提供給其他模塊使用 -
間接訪(fǎng)問(wèn)權(quán)限影響
如果在賦值的時(shí)候設(shè)置了當(dāng)前的變量訪(fǎng)問(wèn)權(quán)限,那么其他的沒(méi)有被修飾的成員變量也會(huì)因此收到同樣的權(quán)限管理,均為修飾變量一樣的訪(fǎng)問(wèn)權(quán)限
struct Point { fileprivate var x = 0 var y = 0 } var p = Point(x: 10, y: 20)
初始化器
如果一個(gè)public
類(lèi)想要在另一個(gè)模塊調(diào)用編譯生成的默認(rèn)無(wú)參初始化器
,必須顯示提供public
的無(wú)參初始化器,因?yàn)?code>public類(lèi)的默認(rèn)初始化器是 internal
訪(fǎng)問(wèn)級(jí)別
注意:
-
required
初始化器必須跟它所屬的類(lèi)擁有相同的訪(fǎng)問(wèn)級(jí)別 - 如果結(jié)構(gòu)體有
private/fileprivate
的存儲(chǔ)實(shí)例屬性,那么它的成員初始化器也是private/fileprivate
,否則默認(rèn)就是internal
枚舉類(lèi)型的
case
訪(fǎng)問(wèn)權(quán)限
- 特點(diǎn)
- 不能給
enum
的每個(gè)case
單獨(dú)設(shè)置訪(fǎng)問(wèn)級(jí)別 - 每個(gè)
case
自動(dòng)接收enum
的訪(fǎng)問(wèn)級(jí)別 -
public enum
定義的case
也是public
- 不能給
協(xié)議的訪(fǎng)問(wèn)權(quán)限
- 特點(diǎn)
- 協(xié)議中定義的內(nèi)容的權(quán)限,將自動(dòng)接收協(xié)議的訪(fǎng)問(wèn)級(jí)別,不能單獨(dú)設(shè)置訪(fǎng)問(wèn)級(jí)別
- 協(xié)議實(shí)現(xiàn)的訪(fǎng)問(wèn)級(jí)別必須 >= 類(lèi)型的訪(fǎng)問(wèn)級(jí)別,或者 >= 協(xié)議的訪(fǎng)問(wèn)級(jí)別
注意:
public
修飾的類(lèi)型/結(jié)構(gòu)體,成員變量以及方法的默認(rèn)類(lèi)型是 internal
類(lèi)型,權(quán)限比public
權(quán)限小,所以此時(shí)遵守的協(xié)議的權(quán)限是 public
,所以會(huì)報(bào)錯(cuò)
public protocol Runnable {
func run()
}
public class Person : Runable {
internal func run {}
}
擴(kuò)展的訪(fǎng)問(wèn)權(quán)限
-
特點(diǎn)
- 如果有顯示設(shè)置擴(kuò)展的訪(fǎng)問(wèn)級(jí)別,擴(kuò)展添加的成員自動(dòng)接收擴(kuò)展級(jí)別
- 如果沒(méi)有顯示設(shè)置擴(kuò)展的訪(fǎng)問(wèn)級(jí)別,擴(kuò)展添加的成員默認(rèn)訪(fǎng)問(wèn)級(jí)別,跟直接在類(lèi)型中定義的成員一樣
- 可以單獨(dú)給擴(kuò)展添加的成員設(shè)置訪(fǎng)問(wèn)級(jí)別
- 不能給用于遵守協(xié)議的擴(kuò)展顯示設(shè)置擴(kuò)展的訪(fǎng)問(wèn)級(jí)別
-
擴(kuò)展的多重聲明
- 在同一個(gè)文件中的擴(kuò)展,可以寫(xiě)成類(lèi)似多個(gè)部分的類(lèi)型聲明
- 在原本的聲明中聲明一個(gè)私有成員,可以在同一文件的擴(kuò)展中訪(fǎng)問(wèn)它
- 擴(kuò)展中聲明一個(gè)私有成員,可以在同一個(gè)文件的其他擴(kuò)展中、原本聲明中訪(fǎng)問(wèn)它
public class Person {
private func run0() {}
private func eat0() {
run1()
}
}
extension Person {
private func run1() {}
private func eat1() {
run0()
}
}
extension Person {
private func eat2() {
run1()
}
}
- 技巧:方法的拆分存儲(chǔ)調(diào)用
有時(shí)候需要將某些方法暫存到變量中,等到合適的實(shí)際動(dòng)態(tài)去調(diào)用它,可以參照如下的方法:
- 將需要調(diào)用的方法取出來(lái)并存到變量中
- 實(shí)例方法所在的類(lèi)并傳入到第一步的方法中,并存儲(chǔ)到變量中
- 通過(guò)第二步存儲(chǔ)的方法進(jìn)行相關(guān)調(diào)用
struct Person {
var age: Int
func run(_ v: Int) { print("func run"), age, v }
}
/*
調(diào)用步驟
*/
var fn1 = Person.run
var fn2 = fn1(Person(age: 10))
fn2(20)
當(dāng)存在兩個(gè)重名的類(lèi)型方法與實(shí)例方法的時(shí)候,在取值的時(shí)候,設(shè)定取值結(jié)果的類(lèi)型來(lái)確定當(dāng)前的靜態(tài)方法或者實(shí)例方法
struct Person {
var age: Int
func run(_ v: Int) { print("func run", age, v) }
static func run(_ v: Int) { print("static func run", v) }
}
//取實(shí)例方法
var fn: (Person) -> (Int) -> () = Person.run
fn(Person(age: 20))(20)
//取靜態(tài)方法
var fn1: (Int)->() = Person.run
fn1(20)
內(nèi)存管理
- 特點(diǎn)
- 跟OC一樣,
Swift
也是采用引用技術(shù)ARC內(nèi)存管理技術(shù)方案來(lái)管理內(nèi)存的(針對(duì)于堆空間) - 引用默認(rèn)都是強(qiáng)引用
- 通過(guò)
weak
定義弱引用(ARC自動(dòng)給弱引用設(shè)置為nil的時(shí)候,不會(huì)觸發(fā)屬性觀察器) - 無(wú)主引用(unowned reference):通過(guò)
unowned
定義無(wú)主引用,改引用不安全,會(huì)產(chǎn)生野指針
-
weak
、unowned
的使用只能使用在類(lèi)的實(shí)例上面
- 跟OC一樣,
-
循環(huán)引用
-
weak
、unowned
可以解決循環(huán)引用問(wèn)題,unowned
要比weak
少一些性能損耗 - 在生命周期中可能會(huì)變成
nil
的時(shí)候使用weak
- 初始化賦值之后再也不會(huì)變成
nil
的時(shí)候使用unowned
-
閉包的循環(huán)引用問(wèn)題
閉包表達(dá)式默認(rèn)會(huì)對(duì)用到的外層對(duì)象產(chǎn)生額外的強(qiáng)引用(對(duì)外層對(duì)象進(jìn)行 retain
操作)
/*
案例一
*/
class Person {
var fn: (()->())?
func run() { print("run") }
deinit { print("deinit") }
}
func test() {
let p = Person()
//會(huì)導(dǎo)致循環(huán)引用問(wèn)題
p.fn = { p.run() }
//解決方法1:weak
p.fn = {[weak p] in
p?.run()
}
//解決方法2:unowned
p.fn = {[unowned p] in
p?.run()
}
//聲明多個(gè)弱引用
p.fn = {[weak wp = p, unowned up = p, a = 10 + 20] in
wp?.run()
}
}
/*
案例二:閉包中引用self, self中引用閉包
*/
class Person1 {
//使用weak去除循環(huán)引用問(wèn)題
lazy var fn: (()->()) = {[weak p = self] in
p?.run()
}
func run() { print("run") }
deinit { print("deinit") }
}
/*
案例三:閉包中引用self之后直接實(shí)例運(yùn)行,不差生循環(huán)引用
以下不會(huì)產(chǎn)生引用循環(huán):
閉包聲明之后立即調(diào)用,調(diào)用完成之后,閉包銷(xiāo)毀,返回閉包結(jié)果,此時(shí)getAge不會(huì)對(duì)閉包產(chǎn)生引用,引用的是閉包返回的值結(jié)果,所以此種情況下不會(huì)出現(xiàn)循環(huán)引用問(wèn)題
*/
class Person2 {
var age: Int = 0
//使用weak去除循環(huán)引用問(wèn)題
lazy var getAge: Int = {
self.age
}()
func run() { print("run") }
deinit { print("deinit") }
}
逃逸閉包
@escaping
- 特點(diǎn)
- 非逃逸閉包、逃逸閉包,一般都是當(dāng)做參數(shù)傳遞給函數(shù)
- 非逃逸閉包:閉包調(diào)用發(fā)生在函數(shù)結(jié)束前,閉包調(diào)用在其作用域范圍內(nèi)
- 逃逸閉包:閉包有可能會(huì)在函數(shù)結(jié)束后調(diào)用,閉包調(diào)用逃離了函數(shù)的作用域,需要通過(guò)
@escaping
聲明修飾
typealias Fn = ()->()
var gFn: Fn?
//非逃逸閉包
func test(_ fn: Fn) { fn() }
//fn逃逸閉包
func test2(_ fn: @escaping Fn) { gFn = fn }
//fn放在dispatch中同樣是逃逸閉包
func test3(_ fn: @escaping Fn) {
DispatchQueue.global().async {
fn()
}
}
func run(){
//這一塊可以根據(jù)具體業(yè)務(wù)來(lái)定,如果想要在異步線(xiàn)程執(zhí)行完成之后強(qiáng)制執(zhí)行當(dāng)前dispatch所在區(qū)域方法,
//那么這里直接引用self確保當(dāng)前作用域范圍內(nèi)能夠執(zhí)行完成并不會(huì)立即銷(xiāo)毀,可以保證在執(zhí)行完成dispatch之后銷(xiāo)毀內(nèi)容達(dá)到延遲銷(xiāo)毀的目的
//如果需要立即中斷,則可以使用弱引用的方式來(lái)完成
DispatchQueue.global().async {[weak p = self] in
p?.fn()
}
}