Swift 面試題---(持續(xù)更新)

1.Class 和 Struct 的區(qū)別

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

  • 結(jié)構(gòu)體不可以繼承

  • 值類型被賦予給一個變量、常量或者被傳遞給一個函數(shù)的時候,其值會被拷貝

  • 引用類型在被賦予到一個變量、常量或者被傳遞到一個函數(shù)時,其值不會被拷貝。因此,引用的是已存在的實(shí)例本身而不是其拷貝

2.理解Swift值類型的寫時復(fù)制

  • 只有當(dāng)一個結(jié)構(gòu)體發(fā)生了寫入行為時才會有復(fù)制行為。

  • 在結(jié)構(gòu)體內(nèi)部用一個引用類型來存儲實(shí)際的數(shù)據(jù),在不進(jìn)行寫入操作的普通傳遞過程中,都是將內(nèi)部的reference的應(yīng)用計數(shù)+1,在進(jìn)行寫入操作時,對內(nèi)部的reference做一次copy操作用來存儲新的數(shù)據(jù),防止和之前的reference產(chǎn)生意外的數(shù)據(jù)共享。

  • swift中提供該[isKnownUniquelyReferenced]函數(shù),他能檢查一個類的實(shí)例是不是唯一的引用,如果是,我們就不需要對結(jié)構(gòu)體實(shí)例進(jìn)行復(fù)制,如果不是,說明對象被不同的結(jié)構(gòu)體共享,這時對它進(jìn)行更改就需要進(jìn)行復(fù)制。

3.defer的用法

  • 使用defer代碼塊來表示在函數(shù)返回前,函數(shù)中最后執(zhí)行的代碼。無論函數(shù)是否會拋出錯誤,這段代碼都將執(zhí)行。

  • defer 語句塊中的代碼, 會在當(dāng)前作用域結(jié)束前調(diào)用。每當(dāng)一個作用域結(jié)束就進(jìn)行該作用域defer執(zhí)行。

    func doSomethingFile{
    openDirectory()
    defer{
        closeDirectory()
    }
    openFile()
    defer{
        closeFile()
    }
    // do other things
    }
    

4.inout 輸入輸出參數(shù)

  • 函數(shù)參數(shù)默認(rèn)為常量。試圖從函數(shù)主體內(nèi)部更改函數(shù)參數(shù)的值會導(dǎo)致編譯時錯誤。這意味著您不能錯誤地更改參數(shù)的值。如果您希望函數(shù)修改參數(shù)的值,并且希望這些更改在函數(shù)調(diào)用結(jié)束后仍然存在,請將該參數(shù)定義為輸入輸出參數(shù)。

  • 您可以通過將inout關(guān)鍵字放在參數(shù)類型的前面來編寫輸入/輸出參數(shù)。一個在出參數(shù)具有傳遞的值中,由函數(shù)修改的功能,并將該部分送回出的功能來代替原來的值。有關(guān)輸入輸出參數(shù)的行為以及相關(guān)的編譯器優(yōu)化的詳細(xì)討論,請參見輸入輸出參數(shù)。

  • 您只能將變量作為輸入輸出參數(shù)的參數(shù)傳遞。您不能將常量或文字值作為參數(shù)傳遞,因為無法修改常量和文字。當(dāng)您將一個與號(&)作為變量傳入in-out參數(shù)時,將它放在變量名的前面,以表明該變量可以被函數(shù)修改。

  • 注意:輸入輸出參數(shù)不能具有默認(rèn)值,并且可變參數(shù)不能標(biāo)記為inout。

    let temporaryA = a
    a = b
    b = temporaryA
    }
    // 參數(shù)a本身定義是常量,inout修飾,可以修改a的值
    var someInt = 3
    var anotherInt = 107
    swapTwoInts(&someInt, &anotherInt)
    print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
    // Prints "someInt is now 107, and anotherInt is now 3"
    

5.什么是高階函數(shù)

  • 一個函數(shù)如果可以以某一個函數(shù)作為參數(shù), 或者是返回值, 那么這個函數(shù)就稱之為高階函數(shù)

6. static和class的區(qū)別

  • 在Swift中static和class都表示“類型范圍作用域”的關(guān)鍵字。在所有類型中(class、static、enum)中,我們可以使用static來描述類型作用域。class是專門用于修飾class類型的。
  • 1.static可以修飾屬性和方法
    • 所修飾的屬性和方法不能夠被重寫。
    • static修飾的類方法和屬性包含了final關(guān)鍵字的特性,重寫會報錯
  • 2.class修飾方法和計算屬性
    • 我們同樣可以使用class修飾方法和計算屬性,但是不能夠修飾存儲屬性。
    • 類方法和計算屬性是可以被重寫的,可以使用class關(guān)鍵字也可以是static

7.自定義模式匹配模式

  • 可參考 swift模式和模式匹配

  • 模式:
    代表單個或者復(fù)合值得結(jié)構(gòu),也就是說模式不是一個特定的值,它是一種抽象的結(jié)構(gòu),【一句話,不是特指,是泛指】。這樣就可以用模式來匹配各種各樣的值。

    • 例如:(x,y)可以匹配元祖(1.2),以及任何包含兩個元素的元組。
      除了利用模式匹配一個值以外,你可以從復(fù)合值中提取出部分或全部值,然后把各個部分的值和一個常量或變量綁定起來。
  • swift中的模式分為兩類:

    • 一種能匹配任何類型的值,另一種在運(yùn)行時匹配某個特定的值,可能會失敗。
    • 第一種模式用于結(jié)構(gòu)簡單變量,常量和可選綁定中的值。此類模式包括通配符模式,標(biāo)識符模式,以及包含前兩種模式的值綁定模式和元組模式。你可以為這類模式指定一個類型標(biāo)注,從而限制它們只能匹配某種特定類型的值。
    • 第二種模式用于全局模式匹配。這種情況下,你試圖匹配的值在運(yùn)行時可能不存在。此類模式包括枚舉用例模式,可選模式,表達(dá)式模式和類型轉(zhuǎn)換模式。你在switch語句的case標(biāo)簽中,do語句的catch 子句中,或者再if,while,guard,for-in語句的case條件語句中使用這類模式。
  • 重載 ~= 該運(yùn)算符

    switch 80 {
       case "eighty":
    //編譯通過并且匹配
       case "not eighty":
       default:
         break
    }
    //以上代碼編譯直接失敗失敗
    //重載 ~= 函數(shù)
    func ~= (pattern: String, value: Int) -> Bool {
       if pattern == "eighty" {
        return value == 80
       } else if pattern == "not eighty" {
            return value != 80
       } else {
            return false
       }
    }
    
    switch 80 {
       case "eighty":
     //編譯通過并且匹配
       case "not eighty":
       default:
         break
    }
    該switch編譯通過
    

8.dynamic framework 和 static framework 的區(qū)別是什么

  • 可參考該文章

  • 靜態(tài)庫和動態(tài)庫, 靜態(tài)庫是每一個程序單獨(dú)打包一份, 而動態(tài)庫則是多個程序之間共享

  • 靜態(tài)庫和動態(tài)庫是相對編譯期和運(yùn)行期的:靜態(tài)庫在程序編譯時會被鏈接到目標(biāo)代碼中,程序運(yùn)行時將不再需要改靜態(tài)庫;而動態(tài)庫在程序編譯時并不會被鏈接到目標(biāo)代碼中,只是在程序運(yùn)行時才被載入,因為在程序運(yùn)行期間還需要動態(tài)庫的存在。

  • 靜態(tài)庫 好處:

    • 模塊化,分工合作,提高了代碼的復(fù)用及核心技術(shù)的保密程度
    • 避免少量改動經(jīng)常導(dǎo)致大量的重復(fù)編譯連接
    • 也可以重用,注意不是共享使用
  • 動態(tài)庫 好處:

    • 使用動態(tài)庫,可以將最終可執(zhí)行文件體積縮小,將整個應(yīng)用程序分模塊,團(tuán)隊合作,進(jìn)行分工,影響比較小
    • 使用動態(tài)庫,多個應(yīng)用程序共享內(nèi)存中得同一份庫文件,節(jié)省資源
    • 使用動態(tài)庫,可以不重新編譯連接可執(zhí)行程序的前提下,更新動態(tài)庫文件達(dá)到更新應(yīng)用程序的目的。
  • 不同點(diǎn):

    • 靜態(tài)庫在鏈接時,會被完整的復(fù)制到可執(zhí)行文件中,如果多個App都使用了同一個靜態(tài)庫,那么每個App都會拷貝一份,缺點(diǎn)是浪費(fèi)內(nèi)存。類似于定義一個基本變量,使用該基本變量是是新復(fù)制了一份數(shù)據(jù),而不是原來定義的;
    • 動態(tài)庫不會復(fù)制,只有一份,程序運(yùn)行時動態(tài)加載到內(nèi)存中,系統(tǒng)只會加載一次,多個程序共用一份,節(jié)約了內(nèi)存。類似于使用變量的內(nèi)存地址一樣,使用的是同一個變量;
  • 共同點(diǎn):

    • 靜態(tài)庫和動態(tài)庫都是閉源庫,只能拿來滿足某個功能的使用,不會暴露內(nèi)部具體的代碼信息

9. Swift 與 Objective-C 的聯(lián)系與區(qū)別?

  • Swift和Objective-C 共用一套運(yùn)行時環(huán)境,Swift 的類型可以橋接到Objective-C(下面我簡稱OC),反之亦然。兩者可以互相引用混合編程。
    其次就是,OC 之前積累的很多類庫,在 Swift 中大部分依然可以直接使用,當(dāng)然,Swift3之后,一些語法改變了很多,不過還是有跡可循的。OC出現(xiàn)過的絕大多數(shù)概念,比如引用計數(shù)、ARC、屬性、協(xié)議、接口、初始化、擴(kuò)展類、命名參數(shù)、匿名函數(shù)等,在Swift中繼續(xù)有效(可能最多換個術(shù)語)。Swift大多數(shù)概念與OC一樣。當(dāng)然Swift也多出了一些新興概念,這些在OC中是沒有的,比如范型、元組等。

10. Swift 比 Objective-C 有什么優(yōu)勢?

  • Swift 容易閱讀,語法和文件結(jié)構(gòu)簡易化。
  • Swift 更易于維護(hù),文件分離后結(jié)構(gòu)更清晰。
  • Swift 更加安全,它是類型安全的語言。
  • Swift 代碼更少,簡潔的語法,可以省去大量冗余代碼。
  • Swift 速度更快,運(yùn)算性能更高。

11.Swift 是面向?qū)ο筮€是函數(shù)式的編程語言?

  • Swift 既是面向?qū)ο蟮?,又是函?shù)式的編程語言。
    說 Swift 是面向?qū)ο蟮恼Z言,是因為 Swift 支持類的封裝、繼承、和多態(tài),從這點(diǎn)上來看與 Java 這類純面向?qū)ο蟮恼Z言幾乎毫無差別。

  • 說 Swift 是函數(shù)式編程語言,是因為 Swift 支持 map, reduce, filter, flatmap 這類去除中間狀態(tài)、數(shù)學(xué)函數(shù)式的方法,更加強(qiáng)調(diào)運(yùn)算結(jié)果而不是中間過程。

12.請說明并比較以下關(guān)鍵詞:Open, Public, Internal, File-private, Private

  • Swift 有五個級別的訪問控制權(quán)限,從高到底依次為比如 Open, Public, Internal, File-private, Private。

  • 他們遵循的基本原則是:高級別的變量不允許被定義為低級別變量的成員變量。比如一個 private 的 class 中不能含有 public 的 String。反之,低級別的變量卻可以定義在高級別的變量中。比如 public 的 class 中可以含有 private 的 Int。

  • Open 具備最高的訪問權(quán)限。其修飾的類和方法可以在任意 Module 中被訪問和重寫;它是 Swift 3 中新添加的訪問權(quán)限。

  • Public 的權(quán)限僅次于 Open。與 Open 唯一的區(qū)別在于它修飾的對象可以在任意 Module 中被訪問,但不能重寫。

  • Internal 是默認(rèn)的權(quán)限。它表示只能在當(dāng)前定義的 Module 中訪問和重寫,它可以被一個 Module 中的多個文件訪問,但不可以被其他的 Module 中被訪問。

  • File-private 也是 Swift 3 新添加的權(quán)限。其被修飾的對象只能在當(dāng)前文件中被使用。例如它可以被一個文件中的 class,extension,struct 共同使用。

  • Private 是最低的訪問權(quán)限。它的對象只能在定義的作用域內(nèi)使用。離開了這個作用域,即使是同一個文件中的其他作用域,也無法訪問。

13.請說明并比較以下關(guān)鍵詞:strong, weak, unowned

  • Swift 的內(nèi)存管理機(jī)制與 Objective-C一樣為 ARC(Automatic Reference Counting)。它的基本原理是,一個對象在沒有任何強(qiáng)引用指向它時,其占用的內(nèi)存會被回收。反之,只要有任何一個強(qiáng)引用指向該對象,它就會一直存在于內(nèi)存中。

  • strong 代表著強(qiáng)引用,是默認(rèn)屬性。當(dāng)一個對象被聲明為 strong 時,就表示父層級對該對象有一個強(qiáng)引用的指向。此時該對象的引用計數(shù)會增加1。

  • weak 代表著弱引用。當(dāng)對象被聲明為 weak 時,父層級對此對象沒有指向,該對象的引用計數(shù)不會增加1。它在對象釋放后弱引用也隨即消失。繼續(xù)訪問該對象,程序會得到 nil,不虧崩潰

  • unowned 與弱引用本質(zhì)上一樣。唯一不同的是,對象在釋放后,依然有一個無效的引用指向?qū)ο螅皇?Optional 也不指向 nil。如果繼續(xù)訪問該對象,程序就會崩潰。

  • 加分回答:

    • weak 和 unowned 的引入是為了解決由 strong 帶來的循環(huán)引用問題。簡單來說,就是當(dāng)兩個對象互相有一個強(qiáng)指向去指向?qū)Ψ?,這樣導(dǎo)致兩個對象在內(nèi)存中無法釋放。

    • weak 和 unowned 的使用場景有如下差別:

      • 當(dāng)訪問對象時該對象可能已經(jīng)被釋放了,則用 weak。比如 delegate 的修飾。
      • 當(dāng)訪問對象確定不可能被釋放,則用 unowned。比如 self 的引用。
      • 實(shí)際上為了安全起見,很多公司規(guī)定任何時候都使用 weak 去修飾。

14. 說說Swift為什么將String,Array,Dictionary設(shè)計成值類型?

要解答這個問題,就要和Objective-C中相同的數(shù)據(jù)結(jié)構(gòu)設(shè)計進(jìn)行比較。Objective-C中,字符串,數(shù)組,字典,皆被設(shè)計為引用類型。

  • 值類型相比引用類型,最大的優(yōu)勢在于內(nèi)存使用的高效。值類型在棧上操作,引用類型在堆上操作。棧上的操作僅僅是單個指針的上下移動,而堆上的操作則牽涉到合并、移位、重新鏈接等。也就是說Swift這樣設(shè)計,大幅減少了堆上的內(nèi)存分配和回收的次數(shù)。同時copy-on-write又將值傳遞和復(fù)制的開銷降到了最低。

  • String,Array,Dictionary設(shè)計成值類型,也是為了線程安全考慮。通過Swift的let設(shè)置,使得這些數(shù)據(jù)達(dá)到了真正意義上的“不變”,它也從根本上解決了多線程中內(nèi)存訪問和操作順序的問題。

  • 設(shè)計成值類型還可以提升API的靈活度。例如通過實(shí)現(xiàn)Collection這樣的協(xié)議,我們可以遍歷String,使得整個開發(fā)更加靈活高效。

15. 閉包是引用類型嗎?

  • 閉包是引用類型。如果一個閉包被分配給一個變量,這個變量復(fù)制給另一個變量,那么他們引用的是同一個閉包,他們的捕捉列表也會被復(fù)制。

16.Swift mutating關(guān)鍵字的使用?

  • 類是引用類型,而結(jié)構(gòu)和枚舉是值類型。默認(rèn)情況下,不能在其實(shí)例方法中修改值類型的屬性。為了修改值類型的屬性,必須在實(shí)例方法中使用mutating關(guān)鍵字。使用此關(guān)鍵字,您的方法將能夠更改屬性的值,并在方法實(shí)現(xiàn)結(jié)束時將其寫回到原始結(jié)構(gòu)

17.Swift定義常量 和 OC定義常量的區(qū)別?

OC:
const int price = 0;
Swift:
let price = 0
  • OC中用 const 來表示常量,而 Swift 中用 let 來判斷是不是常量
  • OC中 const 常量類型和數(shù)值是在編譯時確定的
  • Swift 中 let 常量(只能賦值一次),其類型和值既可以是靜態(tài)的,也可以是一個動態(tài)的計算方法,它們在運(yùn)行時確定的。

18. 閉包

  • 閉包和函數(shù)是引用類型,將函數(shù)或閉包賦值給一個常量還是變量,實(shí)際上都是將常量或變量的值設(shè)置為對應(yīng)函數(shù)或閉包的引用。

    func makeInCount(count: Int) -> () -> Int {
        var total = 0
        func incrementer() -> Int {
            total += count
            return count
        }
        return incrementer
    }
    let incrementBySeven = makeInCount(count: 7)
    incrementBySeven()
    let alsoIncrementBySeven = incrementBySeven
    alsoIncrementBySeven()
    
  • 逃逸閉包,當(dāng)一個閉包作為參數(shù)傳到一個函數(shù)中,但是這個閉包在函數(shù)返回之后才被執(zhí)行,我們稱該閉包從函數(shù)中逃逸。當(dāng)你定義接受閉包作為參數(shù)的函數(shù)時,你可以在參數(shù)名之前標(biāo)注 @escaping,用來指明這個閉包是允許“逃逸”出這個函數(shù)的。 例如網(wǎng)絡(luò)請求??

    func request(result:@escaping((String)->())){
        DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + 10) {
            result("數(shù)據(jù)結(jié)果")
        }
    }
    
  • 非逃逸閉包, 永遠(yuǎn)不會離開一個函數(shù)的局部作用域的閉包就是非逃逸閉包。

    func player(complete:(Bool)->()){
        complete(true)
    }
    
  • 自動閉包,自動閉包是一種自動創(chuàng)建的閉包,用于包裝傳遞給函數(shù)作為參數(shù)的表達(dá)式。這種閉包不接受任何參數(shù),當(dāng)它被調(diào)用的時候,會返回被包裝在其中的表達(dá)式的值。當(dāng)閉包作為參數(shù)傳入 可用@autoclosure標(biāo)記閉包參數(shù) ,可將參數(shù)當(dāng)函數(shù)調(diào)用而并非以閉包的形式。這種便利語法讓你能夠省略閉包的花括號,用一個普通的表達(dá)式來代替顯式的閉包

    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())!")
    // 打印出“Now serving Chris!”
    print(customersInLine.count)
    // 打印出“4”
    // customersInLine is ["Ewa", "Barry", "Daniella"]
    func serve(customer customerProvider: @autoclosure () -> String) {
       print("Now serving \(customerProvider())!")
    }
    serve(customer: customersInLine.remove(at: 0))
    // 打印“Now serving Ewa!”
    // 不用  @autoclosure 修飾
    serve(customer: { customersInLine.remove(at: 0) } )
    // 打印“Now serving Ewa!”
    
    
最后編輯于
?著作權(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)容