Swift 面試題及答案

一、swift訪問級別

Swift 為代碼中的實(shí)體提供了五種不同的訪問級別。這些訪問級別不僅與源文件中定義的實(shí)體相關(guān),同時也與源文件所屬的模塊相關(guān)。

1、訪問權(quán)限修飾

Open 和 Public 級別可以讓實(shí)體被同一模塊源文件中的所有實(shí)體訪問,在模塊外也可以通過導(dǎo)入該模塊來訪問源文件里的所有實(shí)體。通常情況下,你會使用 Open 或 Public 級別來指定框架的外部接口。Open 和 Public 的區(qū)別在后面會提到。
Internal 級別讓實(shí)體被同一模塊源文件中的任何實(shí)體訪問,但是不能被模塊外的實(shí)體訪問。通常情況下,如果某個接口只在應(yīng)用程序或框架內(nèi)部使用,就可以將其設(shè)置為 Internal 級別。
File-private 限制實(shí)體只能在其定義的文件內(nèi)部訪問。如果功能的部分細(xì)節(jié)只需要在文件內(nèi)使用時,可以使用 File-private 來將其隱藏。
Private 限制實(shí)體只能在其定義的作用域,以及同一文件內(nèi)的 extension 訪問。如果功能的部分細(xì)節(jié)只需要在當(dāng)前作用域內(nèi)使用時,可以使用 Private 來將其隱藏。
Open 為最高訪問級別(限制最少),Private 為最低訪問級別(限制最多)。

Open 只能作用于類和類的成員,它和 Public 的區(qū)別如下:

Public 或者其它更嚴(yán)訪問級別的類,只能在其定義的模塊內(nèi)部被繼承。
Public 或者其它更嚴(yán)訪問級別的類成員,只能在其定義的模塊內(nèi)部的子類中重寫。
Open 的類,可以在其定義的模塊中被繼承,也可以在引用它的模塊中被繼承。
Open 的類成員,可以在其定義的模塊中子類中重寫,也可以在引用它的模塊中的子類重寫。
把一個類標(biāo)記為 open,明確的表示你已經(jīng)充分考慮過外部模塊使用此類作為父類的影響,并且設(shè)計(jì)好了你的類的代碼了。

2、訪問級別基本原則

Swift 中的訪問級別遵循一個基本原則:實(shí)體不能定義在具有更低訪問級別(更嚴(yán)格)的實(shí)體中。

例如:

一個 Public 的變量,其類型的訪問級別不能是 Internal,F(xiàn)ile-private 或是 Private。因?yàn)闊o法保證變量的類型在使用變量的地方也具有訪問權(quán)限。
函數(shù)的訪問級別不能高于它的參數(shù)類型和返回類型的訪問級別。因?yàn)檫@樣就會出現(xiàn)函數(shù)可以在任何地方被訪問,但是它的參數(shù)類型和返回類型卻不可以的情況。
關(guān)于此原則在各種情況下的具體表現(xiàn),將在下文有所體現(xiàn)。

3、默認(rèn)訪問級別

如果你沒有為代碼中的實(shí)體顯式指定訪問級別,那么它們默認(rèn)為 internal 級別(有一些例外情況,稍后會進(jìn)行說明)。因此,在大多數(shù)情況下,我們不需要顯式指定實(shí)體的訪問級別。

二、swift 與oc的區(qū)別

swift和OC的主要區(qū)別:

1.base class

Swift中可以定義不繼承于其它類的類,稱之為基類(base class),而OC的類都是繼承自NSObject。

2.switch

在OC中的Switch:

    不能判斷對象類型, 只能判斷基本類型中的整數(shù);可以穿透;default位置可以隨便放。

在Swift中的Switch:

    可以判斷對象類型,支持對象類型、元組、區(qū)間、double類型等等;默認(rèn)不穿透,可以寫fallthough穿透;可以不寫break,如果寫了default,default得放在最后。

3.final關(guān)鍵字

Swift中的final修飾符可以防止類被繼承,還可以防止子類重寫父類的屬性、方法以及下標(biāo)。

4.guard、inout

Swift中,guard關(guān)鍵字只能作用在函數(shù)中,guard關(guān)鍵字必須和else同時出現(xiàn),guard關(guān)鍵字只有在條件為false的時候會走else語句,反之執(zhí)行后邊語句。

Swift中,inout是修飾函數(shù)參數(shù)類型,表示該參數(shù)在函數(shù)內(nèi)修改后(即函數(shù)返回后),其值為修改后的值。

5.運(yùn)算符重載

OC是不支持重載的,Swift是支持重載的。

6.命名空間

OC沒有命名空間,但是在為了區(qū)分在不同框架中可能會使用相同名字的變量沖突的情況,OC使用前綴來區(qū)別。

Swift有命名空間,默認(rèn)就是項(xiàng)目名稱,同一個命名空間全局共享,這也是Swift不需要在import頭文件。

7.swift類型安全,oc更靈活

類型安全語言需要代碼里值的類型非常明確。

8.可選類型

Swift引入了可選項(xiàng)類型,用于處理變量值不存在的情況。Optionals類似于OC中指向nil的指針,但是適用于所有數(shù)據(jù)類型,而非僅僅局限于類,Optionals相比于OC中的nil指針,更加安全和簡明。

9.字符串插值、String.Index

OC:[NSStringstringWithFormat:@"%d",10];

Swift: "\(10)"

10.OC是動態(tài)、Swift是靜態(tài)(函數(shù)內(nèi)聯(lián))

Swift 編譯時就確定了調(diào)用的方法,OC是調(diào)用之前通過虛函數(shù)表來確定調(diào)用的方法。

11.泛型

泛型是用來使代碼能安全工作,Swift中的范型可以在函數(shù)數(shù)據(jù)和普通數(shù)據(jù)類型中使用,例如類、結(jié)構(gòu)體或枚舉。范型可以解決代碼復(fù)用的問題。

12.訪問權(quán)限

Swift又新增了兩種訪問控制權(quán)限 fileprivate和 open。Swift中訪問權(quán)限由大到小依次為:open > public > internal(默認(rèn))> fileprivate >private。

    open:可以在任何地方訪問,包括override和繼承。

    public:可以在任何地方訪問,但其他module中不可以被override和繼承,而在本module內(nèi)可以被override和繼承。

    internal:修飾的屬性或方法在整個模塊內(nèi)都可以訪問,系統(tǒng)默認(rèn)。

    fileprivate:在當(dāng)前文件內(nèi)可以被訪問。

    private:在當(dāng)前類中能被訪問。  

13.enum

Swift 的枚舉成員在被創(chuàng)建時本身就是完備的值,這些值的類型是已經(jīng)明確定義好的 Direction 類型. 不會像 Objective-C 一樣被賦予一個默認(rèn)的整型值。

14.swift 反射機(jī)制

所謂反射就是可以動態(tài)獲取類型、成員信息,同時在運(yùn)行時(而非編譯時)可以動態(tài)調(diào)用任意方法、屬性等行為的特性。反射最重要的目的,就是為了解耦。

在Swift中并不提倡使用Runtime,而是像其他語言一樣使用反射(Reflect)。Swift的反射機(jī)制是基于一個叫 Mirror 的 struct 來實(shí)現(xiàn)的。

Swift其他功能:

1.范圍運(yùn)算符:

a...b 表示 [a,b] 包括a和b 。 (如3...5  就是范圍取3,4,5)

a..<b 表示 [a,b) 包括a,不包括b 。 (如3..<5  就是范圍取3,4)

常見的如for循環(huán):for i in 0...9{}

2.獨(dú)有的元組類型

元組(tuples)把多個值組合成一個復(fù)合值。元組內(nèi)的值可以使任意類型,并不要求是相同類型。

eg: var value = (Int,String) = (x:15,y:"abc")

3.swift中使用let定義常量,var定義變量

使用常量,更加安全,不能夠被修改,在需要對對象進(jìn)行修改的時候只能用var修飾**。**

4.if let 、 guard let

縮減代碼量,安全處理數(shù)據(jù)邏輯,作用是解包。

5.defer

**defer **block 里的代碼會在函數(shù) return 之前執(zhí)行,無論函數(shù)是從哪個分支 return 的,還是有 throw,還是自然而然走到最后一行。    

6.高階函數(shù)

高階函數(shù)僅僅只是一個函數(shù),其可以接收函數(shù)作為參數(shù),或者返回一個函數(shù)來操作其他函數(shù)。Swift的集合類型中就有這些高階函數(shù):Map, FlatMap, Filter, 和Reduce。

Swift優(yōu)點(diǎn):

1.Swift容易閱讀,語法和文件結(jié)構(gòu)簡易化。

2.Swift更易于維護(hù),文件分離后結(jié)構(gòu)更清晰。

3.Swift更加安全,它是類型安全的語言。

4.Swift代碼更少,簡潔的語法,可以省去大量冗余代碼。

5.Swift速度更快,運(yùn)算性能更高。

6.值類型優(yōu)化:如寫時拷貝

三、swift 閉包函數(shù)

1、閉包表達(dá)式種類

逃逸閉包

當(dāng)一個閉包作為參數(shù)傳到一個函數(shù)中,但是這個閉包在函數(shù)返回之后才被執(zhí)行,我們稱該閉包從函數(shù)中逃逸。當(dāng)你定義接受閉包作為參數(shù)的函數(shù)時,你可以在參數(shù)名之前標(biāo)注 @escaping,用來指明這個閉包是允許“逃逸”出這個函數(shù)的。

一種能使閉包“逃逸”出函數(shù)的方法是,將這個閉包保存在一個函數(shù)外部定義的變量中。舉個例子,很多啟動異步操作的函數(shù)接受一個閉包參數(shù)作為 completion handler。這類函數(shù)會在異步操作開始之后立刻返回,但是閉包直到異步操作結(jié)束后才會被調(diào)用。在這種情況下,閉包需要“逃逸”出函數(shù),因?yàn)殚]包需要在函數(shù)返回之后被調(diào)用。例如:

var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlers.append(completionHandler)
}

someFunctionWithEscapingClosure(_:) 函數(shù)接受一個閉包作為參數(shù),該閉包被添加到一個函數(shù)外定義的數(shù)組中。如果你不將這個參數(shù)標(biāo)記為 @escaping,就會得到一個編譯錯誤。

將一個閉包標(biāo)記為 @escaping 意味著你必須在閉包中顯式地引用 self。比如說,在下面的代碼中,傳遞到 someFunctionWithEscapingClosure(_:) 中的閉包是一個逃逸閉包,這意味著它需要顯式地引用 self。相對的,傳遞到 someFunctionWithNonescapingClosure(_:) 中的閉包是一個非逃逸閉包,這意味著它可以隱式引用 self。

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        someFunctionWithEscapingClosure { self.x = 100 }
        someFunctionWithNonescapingClosure { x = 200 }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)
// 打印出“200”

completionHandlers.first?()
print(instance.x)
// 打印出“100”

自動閉包

自動閉包是一種自動創(chuàng)建的閉包,用于包裝傳遞給函數(shù)作為參數(shù)的表達(dá)式。這種閉包不接受任何參數(shù),當(dāng)它被調(diào)用的時候,會返回被包裝在其中的表達(dá)式的值。這種便利語法讓你能夠省略閉包的花括號,用一個普通的表達(dá)式來代替顯式的閉包。

我們經(jīng)常會調(diào)用采用自動閉包的函數(shù),但是很少去實(shí)現(xiàn)這樣的函數(shù)。舉個例子來說,assert(condition:message:file:line:) 函數(shù)接受自動閉包作為它的 condition 參數(shù)和 message 參數(shù);它的 condition 參數(shù)僅會在 debug 模式下被求值,它的 message 參數(shù)僅當(dāng) condition 參數(shù)為 false 時被計(jì)算求值。

自動閉包讓你能夠延遲求值,因?yàn)橹钡侥阏{(diào)用這個閉包,代碼段才會被執(zhí)行。延遲求值對于那些有副作用(Side Effect)和高計(jì)算成本的代碼來說是很有益處的,因?yàn)樗沟媚隳芸刂拼a的執(zhí)行時機(jī)。下面的代碼展示了閉包如何延時求值。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// 打印出“5”

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// 打印出“5”

print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// 打印出“4”

盡管在閉包的代碼中,customersInLine 的第一個元素被移除了,不過在閉包被調(diào)用之前,這個元素是不會被移除的。如果這個閉包永遠(yuǎn)不被調(diào)用,那么在閉包里面的表達(dá)式將永遠(yuǎn)不會執(zhí)行,那意味著列表中的元素永遠(yuǎn)不會被移除。請注意,customerProvider 的類型不是 String,而是 () -> String,一個沒有參數(shù)且返回值為 String 的函數(shù)。

將閉包作為參數(shù)傳遞給函數(shù)時,你能獲得同樣的延時求值行為。

// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// 打印出“Now serving Alex!”

上面的 serve(customer:) 函數(shù)接受一個返回顧客名字的顯式的閉包。下面這個版本的 serve(customer:) 完成了相同的操作,不過它并沒有接受一個顯式的閉包,而是通過將參數(shù)標(biāo)記為 @autoclosure 來接收一個自動閉包?,F(xiàn)在你可以將該函數(shù)當(dāng)作接受 String 類型參數(shù)(而非閉包)的函數(shù)來調(diào)用。customerProvider 參數(shù)將自動轉(zhuǎn)化為一個閉包,因?yàn)樵搮?shù)被標(biāo)記了 @autoclosure 特性。

// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: customersInLine.remove(at: 0))
// 打印“Now serving Ewa!”

注意

過度使用 autoclosures 會讓你的代碼變得難以理解。上下文和函數(shù)名應(yīng)該能夠清晰地表明求值是被延遲執(zhí)行的。

如果你想讓一個自動閉包可以“逃逸”,則應(yīng)該同時使用 @autoclosure@escaping 屬性。@escaping 屬性的講解見上面的 逃逸閉包

// customersInLine i= ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
    customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))

print("Collected \(customerProviders.count) closures.")
// 打印“Collected 2 closures.”
for customerProvider in customerProviders {
    print("Now serving \(customerProvider())!")
}
// 打印“Now serving Barry!”
// 打印“Now serving Daniella!”

在上面的代碼中,collectCustomerProviders(_:) 函數(shù)并沒有調(diào)用傳入的 customerProvider 閉包,而是將閉包追加到了 customerProviders 數(shù)組中。這個數(shù)組定義在函數(shù)作用域范圍外,這意味著數(shù)組內(nèi)的閉包能夠在函數(shù)返回之后被調(diào)用。因此,customerProvider 參數(shù)必須允許“逃逸”出函數(shù)作用域。

四、swift 結(jié)構(gòu)體與類的區(qū)別

Swift 類和結(jié)構(gòu)體的定義方式是比較相似的,從下面代碼可以直觀的看在Swift的類和結(jié)構(gòu)體格式上的區(qū)別。

//類定義
class LGPerson {
    var name: String;
    var age: Int;
    
    init(_ age: Int, _ name: String) {
        self.age = age;
        self.name = name;
    }
    deinit{}
}
// 結(jié)構(gòu)體定義
struct LGPerson {
    var name: String;
    var age: Int;
    
    init(_ age: Int, _ name: String) {
        self.age = age;
        self.name = name;
    }
}

類與結(jié)構(gòu)體的相同點(diǎn)與不同點(diǎn)

相同點(diǎn)

定義存儲值的屬性
定義方法
定義下標(biāo)以使用下標(biāo)語法提供對其值的訪問
定義初始化器
使用 extension 來拓展功能
遵循協(xié)議來提供某種功能

不同點(diǎn)

類有繼承的特性,而結(jié)構(gòu)體沒有
類型轉(zhuǎn)換使您能夠在運(yùn)行時檢查和解釋類實(shí)例的類型
類有析構(gòu)函數(shù)用來釋放其分配的資源
引用計(jì)數(shù)允許對一個類實(shí)例有多個引用
在內(nèi)存上:

    類是引用類型,是一個類類型的變量存儲的是當(dāng)前實(shí)例內(nèi)存地址的引用;

    結(jié)構(gòu)體是一個典型的值類型,是存儲的具體的實(shí)例,可以理解成具體的值。

類與結(jié)構(gòu)體最本質(zhì)的區(qū)別

類是引用類型、結(jié)構(gòu)體是值類型

1、通過修改屬性字段的結(jié)果來看,我們可以把引用類型可以理解成一個在線共享的Excel,其他人對其修改,都是針對的同一個Excel進(jìn)行修改(同一個源文件);而值類型就相當(dāng)于本地的Excel,其他人對其修改,都是從本地Excel拷貝出一份,這樣別人修改了什么,本地的不會感知的(不同的源文件)。

2、存儲位置不同:值類型存儲在棧上,引用類型存儲在堆上。

了解這些之后,我們可以分析出開發(fā)過程中一些不需要繼承特性的、不需要再運(yùn)行時檢查和解釋實(shí)例的,都盡量使用Struct(結(jié)構(gòu)體),因?yàn)橛?jì)算機(jī)的讀寫存儲速度在棧上是大于堆上的讀寫速度,這樣就可以針對開發(fā)過程中的性能優(yōu)化有了一個解決的方向。

x/8g : 可以讀取內(nèi)存中的值(8g表示8字節(jié)格式的輸出)

棧區(qū)Stack、堆區(qū)Heap、全局區(qū)Global

棧主要存放局部變量、方法參數(shù),由系統(tǒng)分配,快速高效
堆區(qū)主要存放對象(內(nèi)存地址)
全局區(qū)存儲全局變量、常量、代碼區(qū)
了解這些之后,我們怎么去得知我們想要的對象是存放在在棧區(qū)還是堆區(qū)呢?這時候我們就可以使用一些llbd的命令去查看。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容