這是卓同學(xué)的 Swift 面試題 強(qiáng)行來答一波叨叙,如有歧義、錯(cuò)誤堪澎、不全擂错,歡迎指(來)正(噴)。
面試題基礎(chǔ)篇有36題樱蛤,高級篇8題钮呀,哲學(xué)篇2題。先就基礎(chǔ)篇前11題進(jìn)行回答昨凡。也為接下來的面試做些準(zhǔn)備 ??
1. class 和 struct 的區(qū)別
2. 不通過繼承爽醋,代碼復(fù)用(共享)的方式有哪些
3. Set 獨(dú)有的方法有哪些?
4. 實(shí)現(xiàn)一個(gè) min 函數(shù)便脊,返回兩個(gè)元素較小的元素
5. map子房、filter、reduce 的作用
6. map 與 flatmap 的區(qū)別
7. 什么是 copy on write
8. 如何獲取當(dāng)前代碼的函數(shù)名和行號
9. 如何聲明一個(gè)只能被類 conform 的 protocol
10. guard 使用場景
11. defer 使用場景
1. class 和 struct 的區(qū)別
此題可參見 The Swift Programming Language就轧,有專門的一節(jié)是用來介紹class 和 struct 的证杭。我也是從中總(搬)結(jié)(運(yùn))來的。
在 Swift 中妒御,class 和 struct 的關(guān)系可以說是非常之親密解愤。
相同點(diǎn): 我們可以使用完全使用相同的語法規(guī)則來為 class 和 struct 定義屬性、方法乎莉、下標(biāo)操作送讲、構(gòu)造器,也可以通過extension 和 protocol 來提供某種功能惋啃。
不同點(diǎn):
1)
與 struct 相比哼鬓,class 還有如下功能:
- 繼承允許一個(gè)類繼承另一個(gè)類的特性
- 類型轉(zhuǎn)換允許在運(yùn)行時(shí)檢查和解釋一個(gè)類實(shí)例的類型
- 析構(gòu)器允許一個(gè)類實(shí)例釋放任何其所被分配的資源
- 引用計(jì)數(shù)允許對一個(gè)類的多次引用
2)
Value Types & Reference Types:
- struct 是值類型 (Value Types) 即:通過被復(fù)制的方式在代碼中傳遞,不使用引用計(jì)數(shù)
- class 是引用類型(Reference Types) 即:引用類型在被賦予到一個(gè)變量边灭、常量或者被傳遞到一個(gè)函數(shù)時(shí)异希,其值不會(huì)被拷貝。因此绒瘦,引用的是已存在的實(shí)例本身而不是其拷貝
3)
分配方式
4)
方法派發(fā)
更多關(guān)于值類型和引用類型的知識称簿,請參見Value Types & Reference Types
2. 不通過繼承,代碼復(fù)用(共享)的方式有哪些
我們都知道代碼復(fù)用的好處:可以降低開發(fā)成本、增加代碼的可靠性并提高它們的一致性。
在Swift中螟深,除了通過繼承猪贪,還可以通過** * 擴(kuò)展、協(xié)議* ** 來實(shí)現(xiàn)代碼復(fù)用。
擴(kuò)展 --- Extensions
擴(kuò)展 就是為一個(gè)已有的類米奸、結(jié)構(gòu)體师倔、枚舉類型或者協(xié)議類型添加新功能悔叽。這包括在沒有權(quán)限獲取原始源代碼的情況下擴(kuò)展類型的能力(即 逆向建模 )航邢。擴(kuò)展和 Objective-C 中的分類類似。
Swift 中的擴(kuò)展可以:
- 添加計(jì)算型屬性和計(jì)算型類型屬性
- 定義實(shí)例方法和類型方法
- 提供新的構(gòu)造器
- 定義下標(biāo)
- 定義和使用新的嵌套類型
- 使一個(gè)已有類型符合某個(gè)協(xié)議
更多擴(kuò)展知識骄蝇,請移步:Extensions
協(xié)議 --- Protocols
協(xié)議 規(guī)定了用來實(shí)現(xiàn)某一特定任務(wù)或者功能的方法膳殷、屬性,以及其他需要的東西九火。類赚窃、結(jié)構(gòu)體或枚舉都可以遵循協(xié)議,并為協(xié)議定義的這些要求提供具體實(shí)現(xiàn)
另外岔激,規(guī)定了用來實(shí)現(xiàn)某一特定任務(wù)或者功能的方法勒极、屬性,以及其他需要的東西虑鼎。類辱匿、結(jié)構(gòu)體或枚舉都可以遵循協(xié)議,并為協(xié)議定義的這些要求提供具體實(shí)現(xiàn)
這里只是簡單敘述下炫彩,更多協(xié)議知識匾七,請移步:Protocols
3. Set 獨(dú)有的方法有哪些
intersect(_:)// 根據(jù)兩個(gè)集合中都包含的值創(chuàng)建的一個(gè)新的集合
exclusiveOr(_:) // 根據(jù)只在一個(gè)集合中但不在兩個(gè)集合中的值創(chuàng)建一個(gè)新的集合
union(_:) // 根據(jù)兩個(gè)集合的值創(chuàng)建一個(gè)新的集合
subtract(_:) //根據(jù)不在該集合中的值創(chuàng)建一個(gè)新的集合
isSubsetOf(_:) //判斷一個(gè)集合中的值是否也被包含在另外一個(gè)集合中
isSupersetOf(_:) //判斷一個(gè)集合中包含的值是否含有另一個(gè)集合中所有的值
isStrictSubsetOf(:) isStrictSupersetOf(:) //判斷一個(gè)集合是否是另外一個(gè)集合的子集合或者父集合并且和特定集合不相等
isDisjointWith(_:) //判斷兩個(gè)集合是否不含有相同的值
let houseAnimals : Set = ["?","?"]
let farmAnimals: Set = ["?","?","?","?","?"]
let cityAnimals: Set = ["?","?"]
houseAnimals. isSubsetOf(farmAnimals)
//true
farmAnimals. isSupersetOf(houseAnimals)
//true
farmAnimals. isDisjointWith(cityAnimals)
//true
4.實(shí)現(xiàn)一個(gè) min 函數(shù),返回兩個(gè)元素較小的元素
func min<T: Comparable>(_ a: T, _ b: T) -> T {
return a < b ? a: b
}
//這里一定要遵守 Comparable 協(xié)議江兢,因?yàn)椴⒉皇撬械念愋投季哂小翱杀刃浴?
5.map昨忆、filter、reduce 的作用
- map 是Array類的一個(gè)方法杉允,我們可以使用它來對數(shù)組的每個(gè)元素進(jìn)行轉(zhuǎn)換
let intArray = [1, 3, 5]
let stringArr = intArray.map {
return "\($0)"
}
// ["1", "3", "5"]
- filter 用于選擇數(shù)組元素中滿足某種條件的元素
let filterArr = intArray.filter {
return $0 > 1
}
//[3, 5]
- reduce 把數(shù)組元素組合計(jì)算為一個(gè)值
let result = intArray.reduce(0) {
return $0 + $1
}
//9
6.map 與 flatmap 的區(qū)別
- map 可以對一個(gè)集合類型的所有元素做一個(gè)映射操作
- 和map 不同邑贴,flatmap 有兩個(gè)定義,分別是:
func flatMap(transform: (Self.Generator.Element) throws -> T?) -> [T]
func flatMap(transform: (Self.Generator.Element) -> S) -> [S.Generator.Element]
let intArray = [1, 2, 3, 4]
result = intArray.flatMap { $0 + 2 }
// [3,4,5,6]
對數(shù)組進(jìn)行flatmap操作叔磷,和map是沒有區(qū)別的
1)
第一種情況返回值類型是 T?, 實(shí)際應(yīng)用中拢驾,可以用來過濾元素為nil的情況,(內(nèi)部使用了 if-let 來對nil值進(jìn)行了過濾) 例如:
let optionalArray: [String?] = ["AA", nil, "BB", "CC"];
var optionalResult = optionalArray.flatMap{ $0 }
// ["AA", "BB", "CC"]
操作前是[String?], 操作后會(huì)變成[String]
2)
第二種情況可以進(jìn)行“降維”操作
let numbersCompound = [[1,2,3],[4,5,6]];
var res = numbersCompound.map { $0.map{ $0 + 2 } }
// [[3, 4, 5], [6, 7, 8]]
var flatRes = numbersCompound.flatMap { $0.map{ $0 + 2 } }
// [3, 4, 5, 6, 7, 8]
0 + 2 } 會(huì)得到[3, 4, 5], [6, 7, 8], 然后遍歷這兩個(gè)數(shù)組改基,將遍歷的元素拼接到一個(gè)新的數(shù)組內(nèi)繁疤,最終并返回就得到了[3, 4, 5, 6, 7, 8]
7.什么是 copy on write
copy on write, 寫時(shí)復(fù)制,簡稱COW寥裂,它通過淺拷貝(shallow copy)只復(fù)制引用而避免復(fù)制值嵌洼;當(dāng)?shù)拇_需要進(jìn)行寫入操作時(shí),首先進(jìn)行值拷貝封恰,在對拷貝后的值執(zhí)行寫入操作,這樣減少了無謂的復(fù)制耗時(shí)褐啡。
應(yīng)用場景 :
寫時(shí)復(fù)制最擅長的是并發(fā)讀取場景诺舔,即多個(gè)線程/進(jìn)程可以通過對一份相同快照,去處理實(shí)效性要求不是很高但是仍然要做的業(yè)務(wù)(比如實(shí)現(xiàn)
FS\DB備份、日志低飒、分析)
適用于對象空間占用大许昨,修改次數(shù)少,而且對數(shù)據(jù)實(shí)效性要求不高的場景
8.如何獲取當(dāng)前代碼的函數(shù)名和行號
- 獲取函數(shù)名:
#function
- 獲取行號:
#line
- 獲取文件名:
#file
- 獲取列:
#column
9.如何聲明一個(gè)只能被類 conform 的 protocol
協(xié)議的繼承列表中褥赊,通過添加 class 關(guān)鍵字來限制協(xié)議只能被類類型遵循糕档,而結(jié)構(gòu)體或枚舉不能遵循該協(xié)議。class 關(guān)鍵字必須第一個(gè)出現(xiàn)在協(xié)議的繼承列表中拌喉,在其他繼承的協(xié)議之前:
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 這里是類類型專屬協(xié)議的定義部分
}
10.guard 使用場景
使用 guard 來表達(dá) “提前退出”的意圖速那,有以下使用場景 :
- 在驗(yàn)證入口條件時(shí)
- 在成功路徑上提前退出
- 在可選值解包時(shí)(拍扁 if let..else 金字塔)
- return 和 throw 中
- 日志、崩潰和斷言中
而下面則是盡量避免使用的場景: - 不要用 guard :替代瑣碎的 if..else 語句
- 不要用 guard :作為 if 的相反情況
- 不要:在 guard 的 else 語句中放入復(fù)雜代碼
具體參見 使用 guard 的正確姿勢
11.defer 使用場景
defer 語句用于在退出當(dāng)前作用域之前執(zhí)行代碼.例如:
手動(dòng)管理資源時(shí)尿背,比如 關(guān)閉文件描述符端仰,或者即使拋出了錯(cuò)誤也需要執(zhí)行一些操作時(shí),就可以使用 defer 語句田藐。
如果多個(gè) defer 語句出現(xiàn)在同一作用域內(nèi)荔烧,那么它們執(zhí)行的順序與出現(xiàn)的順序相反
func f() {
defer { print("First") }
defer { print("Second") }
defer { print("Third") }
}
f()
// 打印 “Third”
// 打印 “Second”
// 打印 “First”
詳細(xì)講解請參考喵神的文章:關(guān)于 Swift defer 的正確使用