- 總結(jié)關(guān)于swift的面試題------持續(xù)更新
- 來源于網(wǎng)上、書籍等
- 侵權(quán)即刪-聯(lián)系我
1.Class 和 Struct 的區(qū)別
類是引用類型, 結(jié)構(gòu)體為值類型
結(jié)構(gòu)體不可以繼承
值類型被賦予給一個變量寞秃、常量或者被傳遞給一個函數(shù)的時候沟娱,其值會被拷貝
引用類型在被賦予到一個變量、常量或者被傳遞到一個函數(shù)時,其值不會被拷貝。因此拟逮,引用的是已存在的實例本身而不是其拷貝
2.理解Swift值類型的寫時復(fù)制
只有當(dāng)一個結(jié)構(gòu)體發(fā)生了寫入行為時才會有復(fù)制行為。
在結(jié)構(gòu)體內(nèi)部用一個引用類型來存儲實際的數(shù)據(jù)剥槐,在不進行寫入操作的普通傳遞過程中唱歧,都是將內(nèi)部的reference的應(yīng)用計數(shù)+1宪摧,在進行寫入操作時粒竖,對內(nèi)部的reference做一次copy操作用來存儲新的數(shù)據(jù),防止和之前的reference產(chǎn)生意外的數(shù)據(jù)共享几于。
swift中提供該[isKnownUniquelyReferenced]函數(shù)蕊苗,他能檢查一個類的實例是不是唯一的引用,如果是沿彭,我們就不需要對結(jié)構(gòu)體實例進行復(fù)制朽砰,如果不是,說明對象被不同的結(jié)構(gòu)體共享,這時對它進行更改就需要進行復(fù)制瞧柔。
3.defer的用法
使用defer代碼塊來表示在函數(shù)返回前漆弄,函數(shù)中最后執(zhí)行的代碼。無論函數(shù)是否會拋出錯誤造锅,這段代碼都將執(zhí)行撼唾。
-
defer 語句塊中的代碼, 會在當(dāng)前作用域結(jié)束前調(diào)用。每當(dāng)一個作用域結(jié)束就進行該作用域defer執(zhí)行哥蔚。
func doSomethingFile{ openDirectory() defer{ closeDirectory() } openFile() defer{ closeFile() } // do other things }
4.inout 輸入輸出參數(shù)
函數(shù)參數(shù)默認為常量倒谷。試圖從函數(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)化的詳細討論,請參見輸入輸出參數(shù)捡需。
您只能將變量作為輸入輸出參數(shù)的參數(shù)傳遞办桨。您不能將常量或文字值作為參數(shù)傳遞,因為無法修改常量和文字站辉。當(dāng)您將一個與號(&)作為變量傳入in-out參數(shù)時呢撞,將它放在變量名的前面,以表明該變量可以被函數(shù)修改饰剥。
-
注意:輸入輸出參數(shù)不能具有默認值殊霞,并且可變參數(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ù)合值中提取出部分或全部值凰盔,然后把各個部分的值和一個常量或變量綁定起來。
- 例如:(x,y)可以匹配元祖(1.2)监徘,以及任何包含兩個元素的元組。
-
swift中的模式分為兩類:
- 一種能匹配任何類型的值倦春,另一種在運行時匹配某個特定的值户敬,可能會失敗。
- 第一種模式用于結(jié)構(gòu)簡單變量睁本,常量和可選綁定中的值尿庐。此類模式包括通配符模式,標(biāo)識符模式呢堰,以及包含前兩種模式的值綁定模式和元組模式抄瑟。你可以為這類模式指定一個類型標(biāo)注,從而限制它們只能匹配某種特定類型的值枉疼。
- 第二種模式用于全局模式匹配皮假。這種情況下,你試圖匹配的值在運行時可能不存在骂维。此類模式包括枚舉用例模式惹资,可選模式,表達式模式和類型轉(zhuǎn)換模式航闺。你在switch語句的case標(biāo)簽中褪测,do語句的catch 子句中,或者再if,while,guard,for-in語句的case條件語句中使用這類模式潦刃。
-
重載 ~= 該運算符
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)庫是每一個程序單獨打包一份, 而動態(tài)庫則是多個程序之間共享
靜態(tài)庫和動態(tài)庫是相對編譯期和運行期的:靜態(tài)庫在程序編譯時會被鏈接到目標(biāo)代碼中侮措,程序運行時將不再需要改靜態(tài)庫;而動態(tài)庫在程序編譯時并不會被鏈接到目標(biāo)代碼中福铅,只是在程序運行時才被載入萝毛,因為在程序運行期間還需要動態(tài)庫的存在。
-
靜態(tài)庫 好處:
- 模塊化滑黔,分工合作笆包,提高了代碼的復(fù)用及核心技術(shù)的保密程度
- 避免少量改動經(jīng)常導(dǎo)致大量的重復(fù)編譯連接
- 也可以重用,注意不是共享使用
-
動態(tài)庫 好處:
- 使用動態(tài)庫略荡,可以將最終可執(zhí)行文件體積縮小庵佣,將整個應(yīng)用程序分模塊,團隊合作汛兜,進行分工巴粪,影響比較小
- 使用動態(tài)庫,多個應(yīng)用程序共享內(nèi)存中得同一份庫文件粥谬,節(jié)省資源
- 使用動態(tài)庫肛根,可以不重新編譯連接可執(zhí)行程序的前提下,更新動態(tài)庫文件達到更新應(yīng)用程序的目的漏策。
-
不同點:
- 靜態(tài)庫在鏈接時派哲,會被完整的復(fù)制到可執(zhí)行文件中,如果多個App都使用了同一個靜態(tài)庫掺喻,那么每個App都會拷貝一份芭届,缺點是浪費內(nèi)存。類似于定義一個基本變量感耙,使用該基本變量是是新復(fù)制了一份數(shù)據(jù)褂乍,而不是原來定義的;
- 動態(tài)庫不會復(fù)制即硼,只有一份逃片,程序運行時動態(tài)加載到內(nèi)存中,系統(tǒng)只會加載一次只酥,多個程序共用一份题诵,節(jié)約了內(nèi)存。類似于使用變量的內(nèi)存地址一樣层皱,使用的是同一個變量性锭;
-
共同點:
- 靜態(tài)庫和動態(tài)庫都是閉源庫,只能拿來滿足某個功能的使用叫胖,不會暴露內(nèi)部具體的代碼信息
9. Swift 與 Objective-C 的聯(lián)系與區(qū)別草冈?
- Swift和Objective-C 共用一套運行時環(huán)境,Swift 的類型可以橋接到Objective-C(下面我簡稱OC)瓮增,反之亦然怎棱。兩者可以互相引用混合編程。
其次就是绷跑,OC 之前積累的很多類庫拳恋,在 Swift 中大部分依然可以直接使用,當(dāng)然砸捏,Swift3之后谬运,一些語法改變了很多隙赁,不過還是有跡可循的。OC出現(xiàn)過的絕大多數(shù)概念梆暖,比如引用計數(shù)伞访、ARC、屬性轰驳、協(xié)議厚掷、接口、初始化级解、擴展類冒黑、命名參數(shù)、匿名函數(shù)等勤哗,在Swift中繼續(xù)有效(可能最多換個術(shù)語)抡爹。Swift大多數(shù)概念與OC一樣。當(dāng)然Swift也多出了一些新興概念俺陋,這些在OC中是沒有的豁延,比如范型、元組等腊状。
10. Swift 比 Objective-C 有什么優(yōu)勢诱咏?
- Swift 容易閱讀,語法和文件結(jié)構(gòu)簡易化缴挖。
- Swift 更易于維護袋狞,文件分離后結(jié)構(gòu)更清晰。
- Swift 更加安全映屋,它是類型安全的語言苟鸯。
- Swift 代碼更少,簡潔的語法棚点,可以省去大量冗余代碼早处。
- Swift 速度更快,運算性能更高瘫析。
11.Swift 是面向?qū)ο筮€是函數(shù)式的編程語言?
Swift 既是面向?qū)ο蟮钠霭穑质呛瘮?shù)式的編程語言。
說 Swift 是面向?qū)ο蟮恼Z言贬循,是因為 Swift 支持類的封裝咸包、繼承、和多態(tài)杖虾,從這點上來看與 Java 這類純面向?qū)ο蟮恼Z言幾乎毫無差別烂瘫。說 Swift 是函數(shù)式編程語言,是因為 Swift 支持 map, reduce, filter, flatmap 這類去除中間狀態(tài)奇适、數(shù)學(xué)函數(shù)式的方法坟比,更加強調(diào)運算結(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 是默認的權(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)存管理機制與 Objective-C一樣為 ARC(Automatic Reference Counting)窑滞。它的基本原理是琼牧,一個對象在沒有任何強引用指向它時,其占用的內(nèi)存會被回收葛假。反之障陶,只要有任何一個強引用指向該對象,它就會一直存在于內(nèi)存中聊训。
strong 代表著強引用抱究,是默認屬性。當(dāng)一個對象被聲明為 strong 時带斑,就表示父層級對該對象有一個強引用的指向鼓寺。此時該對象的引用計數(shù)會增加1勋拟。
weak 代表著弱引用。當(dāng)對象被聲明為 weak 時妈候,父層級對此對象沒有指向敢靡,該對象的引用計數(shù)不會增加1。它在對象釋放后弱引用也隨即消失苦银。繼續(xù)訪問該對象啸胧,程序會得到 nil,不虧崩潰
unowned 與弱引用本質(zhì)上一樣幔虏。唯一不同的是纺念,對象在釋放后,依然有一個無效的引用指向?qū)ο笙肜ǎ皇?Optional 也不指向 nil陷谱。如果繼續(xù)訪問該對象,程序就會崩潰瑟蜈。
-
加分回答:
weak 和 unowned 的引入是為了解決由 strong 帶來的循環(huán)引用問題烟逊。簡單來說,就是當(dāng)兩個對象互相有一個強指向去指向?qū)Ψ狡谈@樣導(dǎo)致兩個對象在內(nèi)存中無法釋放宪躯。
-
weak 和 unowned 的使用場景有如下差別:
- 當(dāng)訪問對象時該對象可能已經(jīng)被釋放了,則用 weak夷都。比如 delegate 的修飾眷唉。
- 當(dāng)訪問對象確定不可能被釋放,則用 unowned囤官。比如 self 的引用冬阳。
- 實際上為了安全起見,很多公司規(guī)定任何時候都使用 weak 去修飾党饮。
14. 說說Swift為什么將String肝陪,Array,Dictionary設(shè)計成值類型刑顺?
要解答這個問題氯窍,就要和Objective-C中相同的數(shù)據(jù)結(jié)構(gòu)設(shè)計進行比較。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ù)達到了真正意義上的“不變”腻暮,它也從根本上解決了多線程中內(nèi)存訪問和操作順序的問題彤守。
設(shè)計成值類型還可以提升API的靈活度。例如通過實現(xiàn)Collection這樣的協(xié)議哭靖,我們可以遍歷String具垫,使得整個開發(fā)更加靈活高效。
15. 閉包是引用類型嗎试幽?
- 閉包是引用類型筝蚕。如果一個閉包被分配給一個變量,這個變量復(fù)制給另一個變量铺坞,那么他們引用的是同一個閉包起宽,他們的捕捉列表也會被復(fù)制。
16.Swift mutating關(guān)鍵字的使用济榨?
- 類是引用類型坯沪,而結(jié)構(gòu)和枚舉是值類型。默認情況下擒滑,不能在其實例方法中修改值類型的屬性腐晾。為了修改值類型的屬性,必須在實例方法中使用mutating關(guān)鍵字丐一。使用此關(guān)鍵字藻糖,您的方法將能夠更改屬性的值,并在方法實現(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)的計算方法,它們在運行時確定的潘拱。
18. 閉包
-
閉包和函數(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é)果") } }
-
非逃逸閉包
, 永遠不會離開一個函數(shù)的局部作用域的閉包就是非逃逸閉包。func player(complete:(Bool)->()){ complete(true) }
-
自動閉包
懦趋,自動閉包是一種自動創(chuàng)建的閉包晾虑,用于包裝傳遞給函數(shù)作為參數(shù)的表達式。這種閉包不接受任何參數(shù)仅叫,當(dāng)它被調(diào)用的時候帜篇,會返回被包裝在其中的表達式的值。當(dāng)閉包作為參數(shù)傳入 可用@autoclosure標(biāo)記閉包參數(shù) 诫咱,可將參數(shù)當(dāng)函數(shù)調(diào)用而并非以閉包的形式笙隙。這種便利語法讓你能夠省略閉包的花括號,用一個普通的表達式來代替顯式的閉包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!”