譯文:Swift Interview Questions and Answers
Swift面試問題及答案-part2
原文鏈接 : Swift Interview Questions and Answers
譯文發(fā)布地址:
part1: http://www.itdecent.cn/p/e98d7dc625ff
part2:http://www.itdecent.cn/p/0b9bdffc2523
原文作者 : Antonio Bello
譯者 : lfb_CD
歡迎關(guān)注我的微博:http://weibo.com/lfbWb
寫在前面的話:
譯文中有許多鏈接和代碼是我為方便讀者閱讀添加的---我可是一名有情懷的譯者
Verbal questions 口頭的提問
到達(dá)這一步你已經(jīng)很優(yōu)秀了,但是你還不能自稱為絕地武士(意思是還不能認(rèn)為自己很厲害了)任何人只要夠努力都可以解決上面那些代碼,但是你如何處理下面這些不斷出現(xiàn)的理論知識和實(shí)踐問題呢
回答這些問題仍然需要你在Playground里實(shí)際操作
初級
Question #1 - Swift 1.0 or later
什么是可選數(shù)據(jù)類型?它解決了什么問題?
答案:
可選數(shù)據(jù)類型是用來表示一個數(shù)據(jù)類型缺少具體值。在Objective-C中,只有引用類型才允許沒有具體值,用的是nil來表示。比如float數(shù)據(jù)類型沒有這樣的特性
Swift用可選數(shù)據(jù)類型擴(kuò)展了值類型和引用數(shù)據(jù)類型??蛇x數(shù)據(jù)類型任何時(shí)候都允許擁有具體值或者為
nil
Question #2 - Swift 1.0 or later
什么時(shí)候使用結(jié)構(gòu)體,我們什么時(shí)候又應(yīng)該使用類呢?
答案:
有一個正在進(jìn)行的討論,關(guān)于過度使用類超過結(jié)構(gòu)體這究竟是好還是壞。
函數(shù)式編程傾向于多使用值數(shù)據(jù)類型,而面向?qū)ο缶幊谈鼉A向于使用類。
在Swift中,類和結(jié)構(gòu)體有很多不同的特性??梢缘贸鱿旅孢@樣一份總結(jié):
- 類支持繼承,結(jié)構(gòu)體不行
- 類是引用類型,結(jié)構(gòu)體是值類型
我們并沒有一個規(guī)則來判定使用哪一個是最好。一般的建議是在能夠達(dá)到你目標(biāo)的前提下且使用到的代價(jià)最小(結(jié)構(gòu)體比類節(jié)省內(nèi)存空間)。除非你需要用到繼承或者是引用的語法,否則那就采用結(jié)構(gòu)體。
更多關(guān)于類和結(jié)構(gòu)體的細(xì)節(jié)問題請查閱這篇文章:
detailed post on the matter.(我們還在翻譯中..)
注意:在運(yùn)行時(shí),結(jié)構(gòu)體比類具有更高性能的因?yàn)榻Y(jié)構(gòu)體的方法調(diào)用是靜態(tài)綁定的,而類的方法調(diào)用是在運(yùn)行時(shí)動態(tài)解析。這是另一個很好的理由來使用結(jié)構(gòu)體,而不是使用類。
Question #3 - Swift 1.0 or later
什么是泛型,它們又解決了什么問題?
答案:
泛型是用來使代碼能安全工作。在Swift中,泛型可以在函數(shù)數(shù)據(jù)類型和普通數(shù)據(jù)類型中使用,例如類、結(jié)構(gòu)體或枚舉。
泛型解決了代碼復(fù)用的問題。有一種常見的情況,你有一個方法,需要一個類型的參數(shù),你為了適應(yīng)另一種類型的參數(shù)還得重新再寫一遍這個方法。
比如,在下面的代碼中,第二個方法是第一個方法的“克隆體”:
func areIntEqual(x: Int, _ y: Int) -> Bool {
return x == y
}
func areStringsEqual(x: String, _ y: String) -> Bool {
return x == y
}
areStringsEqual("ray", "ray") // true
areIntEqual(1, 1) // true
>一個Objective-C開發(fā)者可能會采用`NSObject`來解決問題:
>
import Foundation
func areTheyEqual(x: NSObject, _ y: NSObject) -> Bool {
return x == y
}
areTheyEqual("ray", "ray") // true
areTheyEqual(1, 1) // true
>這段代碼能達(dá)到了目的,但是編譯的時(shí)候并不安全。它允許一個字符串和一個整型數(shù)據(jù)進(jìn)行比較:
>
areTheyEqual(1, "ray")
程序可能不會崩潰,但是允許一個字符串和一個整型數(shù)據(jù)進(jìn)行比較可能不會得到想要的結(jié)果。
采用泛型的話,你可以將上面兩個方法合并為一個,并同時(shí)還保證了數(shù)據(jù)類型安全。這是實(shí)現(xiàn)代碼:
>
func areTheyEqual<T: Equatable>(x: T, _ y: T) -> Bool {
return x == y
}
areTheyEqual("ray", "ray")
areTheyEqual(1, 1)
### Question #4 - Swift 1.0 or later
偶爾你也會不可避免地使用隱式可選類型。那請問什么時(shí)候我們需要這么做?為什么需要這么做?
>#### 答案:
>最常見的情況是:
>>1. 不為nil的才能初始化它的值。一個典型的例子是一個界面生成器的出口(Interface Builder outlet),它總是在它的本體初始化后初始化。在這種情況下,如果它在界面構(gòu)建器(Interface Builder)中正確地配置了,就能夠保證在使用前outlet不為nil的。
>>
>>2.為解決強(qiáng)引用循環(huán)問題(不知道循環(huán)引用是什么的可以看我們翻譯的Swift官方文檔[自動引用計(jì)數(shù)篇](http://wiki.jikexueyuan.com/project/swift/chapter2/16_Automatic_Reference_Counting.html))。當(dāng)2個實(shí)例互相引用時(shí),就需要一個不為nil的引用指向另一個實(shí)例。引用的一邊可以修飾為unowned,另一邊使用隱式可選類型,便可解決循環(huán)引用問題。
>>為方便大家理解我貼段代碼上來(原文是沒用的)
>>```
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
理解得不太清楚可以點(diǎn)開上面鏈接查看
小貼士:盡量不要使用隱式可選類型。使用它們會增加運(yùn)行時(shí)崩潰的幾率。在某些情況下,出現(xiàn)崩潰也許是程序員需要這么做,這里也有一個更好的方法來達(dá)到同樣的效果,例如,使用fatalerror()。
Question #5 - Swift 1.0 or later
你知道有哪些解包的方式?它們是否是安全解包的?
答案:
ps:下面代碼為譯者本人為方便讀者閱讀而添加,如還有不理解的地方可以根據(jù)關(guān)鍵字搜索相關(guān)文檔
強(qiáng)制用!展開 -- 操作不安全
聲明隱式可選類型變量 -- 在許多情況下是不安全的
(var implicitlyUnwrappedString: String!)
>* optional binding -- 安全
>
> ```
var count: Int?
count = 100
if let validCount = count {
"count is " + String(validCount) //count is 100
} else {
"nil"
}
新的Swift2 guard聲明 -- 安全
自判斷鏈接--安全
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has (roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
>* nil -- 安全
## 中級
漸漸地你挑戰(zhàn)了這里。也許你之前問題解決得很好,但是讓我們看看你是否能很好地通過下面這些問題。
### Question #1 - Swift 1.0 or later
Swift是一種面向?qū)ο蟮恼Z言還是一種面向函數(shù)的語言?
>#### 答案:
Swift是一種混合語言,同時(shí)支持這兩種范式。
>它實(shí)現(xiàn)了三種面向?qū)ο蟮幕驹瓌t
>
* 封裝
* 繼承
* 多態(tài)
>
當(dāng)Swift作為一種面向函數(shù)的語言來理解時(shí),會有不同卻相似的方式來定義它。
其中一種是較為常見的維基百科上的:"…a programming paradigm [...] that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data."
“……編程范式[……],將數(shù)學(xué)函數(shù)作為一種值,不需要考慮其中的狀態(tài)變化和數(shù)據(jù)變化。”
>你要覺得Swift是一個門成熟的面向函數(shù)的編程語言是比較牽強(qiáng)的,但它確實(shí)也具有很多面向函數(shù)編程的基本要素。
### Question #2 - Swift 1.0 or later
下列哪些特性是Swift含有的?
1. 泛型類
2. 泛型結(jié)構(gòu)
3. 泛型接口
>#### 答案:
>* Swift中包括了上述的1和2。泛型可以在類,結(jié)構(gòu)體,枚舉全局方法或者普通方法中使用。
>* 3用typealias實(shí)現(xiàn)了。它本身不是一個泛型類型,它是一個占位符名稱。它通常被稱為關(guān)聯(lián)類型,當(dāng)一個協(xié)議聲明時(shí)使用。如果有不明白的可查看[喵神的文章](http://swifter.tips/typealias/)
### Question #3 - Swift 1.0 or later
在Objective-C語言中,常量可以被定義如下:
const int number = 0;
這是Swift對應(yīng)的代碼:
let number = 0
請問它們有什么區(qū)別么?如果有,你能解釋下它們之間的區(qū)別么?
>#### 答案:
`const` 是一個變量在編譯時(shí)初始化的值或著是在編譯時(shí)解決初始化的。
>
`let`聲明的常數(shù)是在運(yùn)行的時(shí)候創(chuàng)建,它最終可以被初始化為靜態(tài)或者動態(tài)的表達(dá)式。注意它的值只能被分配一次。
### Question #4 - Swift 1.0 or later
Swift聲明一個靜態(tài)屬性或靜態(tài)函數(shù)可以使用static來修飾。這是一個結(jié)構(gòu)體的例子:
struct Sun {
static func illuminate() {}
}
對于類,可以使用static或class來修飾。他們可以達(dá)到同樣的目標(biāo),但實(shí)際上他們是不同的。你能解釋他們有什么不同嗎?
>#### 答案:
使用static聲明的一個靜態(tài)屬性或者方法并不可被覆蓋override(子類覆蓋父類的方法)。
使用class就可以覆蓋。
>
當(dāng)用在類里的時(shí)候,static相當(dāng)于class final
比如在下面這段代碼中你如果覆蓋`illuminate()`編譯器就會報(bào)錯
>
class Star {
class func spin() {}
static func illuminate() {}
}
class Sun : Star {
override class func spin() {
super.spin()
}
override static func illuminate() { // error: class method overrides a 'final' class method
super.illuminate()
}
}
### Question #5 - Swift 1.0 or later
可以使用擴(kuò)展添加存儲屬性嗎?
>#### 答案:
no,這是不可能的。擴(kuò)展可以為已經(jīng)存在的數(shù)據(jù)類型添加新的行為,但是不允許改變類型本身或它的接口。
如果您添加了存儲的屬性,您需要額外的內(nèi)存來存儲新的值。擴(kuò)展不能完成這樣的任務(wù)。
##高級
噢,孩子,你是個聰明的人,對嗎?那就一步一步向上攀爬吧。
### Question #1 - Swift 1.2 or later
在Swift1.2中,你可以解釋一下聲明一個枚舉類型的泛型的問題么?
以一個含有兩個泛型參數(shù)`T`和`V`的枚舉`Either`為例,用`T`作為`Left`的相關(guān)值類型,`V`作為`Right`的相關(guān)值類型:
enum Either<T, V> {
case Left(T)
case Right(V)
}
小貼士:檢查這種情況應(yīng)該在一個Xcode中的項(xiàng)目中,不是在Playground上。還得注意,這個問題是Swift 1.2相關(guān)的,所以你需要Xcode 6.4。
>#### 答案:
編譯失敗的錯誤消息:
>
unimplemented IR generation feature non-fixed multi-payload enum layout
出現(xiàn)的問題是,不能確定`T`需要的內(nèi)存大小。分配內(nèi)存大小時(shí)取決于`T`本身的數(shù)據(jù)類型,枚舉需要一個可知的固定大小的值類型。
>
最常用的解決方法是把泛型用引用類型進(jìn)行包裝,一般起名為`Box`,代碼如下:
>
class Box<T> {
let value: T
init(_ value: T) {
self.value = value
}
}
enum Either<T, V> {
case Left(Box<T>)
case Right(Box<V>)
}
這個問題只在Swift 1.0或者以上出現(xiàn),但是2.0已經(jīng)解決了。
### Question #2 - Swift 1.0 or later
閉包是值類型還是引用類型的?
>#### 答案:
閉包是引用類型。如果一個閉包被分配給一個變量,該變量被復(fù)制到另一個變量,它們實(shí)際是引用的相同一個閉包并且它里面的參數(shù)列表也同樣會被復(fù)制。
### Question #3 - Swift 1.0 or later
`UInt`數(shù)據(jù)類型用于存儲整數(shù)。它實(shí)現(xiàn)了從一個帶符號的整數(shù)轉(zhuǎn)化為`UInt`的初始化方式:
init(_ value: Int)
然而,如果您提供了一個負(fù)值,例如下面的代碼會產(chǎn)生一個編譯錯誤:
let myNegative = UInt(-1)
我們知道負(fù)數(shù)在內(nèi)部是使用補(bǔ)碼表示,你怎么才能把一個負(fù)數(shù)的`Int`轉(zhuǎn)化為`UInt`,同時(shí)保持它在內(nèi)存中的表示形式?
>#### 答案:
這兒有一個初始化的方式:
>
UInt(bitPattern: Int)
### Question #4 - Swift 1.0 or later
你能描述一下你用Swift時(shí)遇到的循環(huán)引用么?你是怎么解決的?
>#### 答案:
循環(huán)引用是指兩個實(shí)例彼此強(qiáng)引用,導(dǎo)致內(nèi)存泄漏,因?yàn)閮蓚€實(shí)例都不會被收回。原因是只要有一個強(qiáng)引用,實(shí)例就不會被回收。你可以通過 `weak` 或者 `unowned` 引用 替換其中一個強(qiáng)引用,從而打破強(qiáng)引用循環(huán)
>
想了解更多可以查看我們翻譯的Swift官方文檔里的有關(guān)章節(jié)[自動引用計(jì)數(shù)篇](http://wiki.jikexueyuan.com/project/swift/chapter2/16_Automatic_Reference_Counting.html)
### Question #5 - Swift 2.0 or later
Swift2.0增添了一個新關(guān)鍵字實(shí)現(xiàn)遞歸枚舉。這里有一個枚舉包含一個`Node`,`Node`有兩個的相關(guān)的值類型,`T`和`List`:
enum List<T> {
case Node(T, List<T>)
}
請問那個可以實(shí)現(xiàn)遞歸枚舉的關(guān)鍵字是什么?
>#### 答案:
>
`indirect`
>
代碼如下:
>
enum List<T> {
indirect case Cons(T, List<T>)
}
# 之后的方向?
恭喜你看到了文章末尾,如果你確實(shí)不太清楚那些答案,希望你也不要感到沮喪!
其中的一些問題很復(fù)雜,Swift是一個非常豐富、且富有表現(xiàn)力的語言。我們還有很多需要學(xué)。此外,蘋果不斷改善Swift與添加新的功能,所以非常有可能還存在一些非常好用的但是我們并不知道的(言外之意就是讓我們多研究)。
To get to know Swift or build upon what you already know, be sure to check out our in-depth, tutorial-rich book, [Swift by Tutorials](http://www.raywenderlich.com/store/swift-by-tutorials), or sign up for our hands-on tutorial conference [RWDevCon](http://www.rwdevcon.com/)!
當(dāng)然,最根本的資料當(dāng)然還是由蘋果公司寫撰寫的[The Swift Programming Language](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/) (ps:肯定還有我們翻譯的[Swift中文版](http://wiki.jikexueyuan.com/project/swift/)撒!)
最后,使用一門語言才是學(xué)習(xí)語言最好的方式。只需要你在Playground上寫寫或在一個真正的項(xiàng)目使用Swift。Swift幾乎可以與objective-c無縫地混合,所以建立一個你已經(jīng)非常熟悉的現(xiàn)有項(xiàng)目是一個很好的方法來學(xué)習(xí)Swift的來龍去脈。
感謝您的閱讀和解決以上這些問題所做出的努力!你也可以在評論中留下你的問題或者一些你的發(fā)現(xiàn)。我也不介意你提出一些你項(xiàng)目中遇到的問題。我們可以互相學(xué)習(xí)。咱們論壇上見!
***
第一次翻譯這么長的文章,也許會有翻譯錯誤的地方,大家可以在評論中幫忙指出。
另外,我在管理一個微信公眾號SwiftTips,每天發(fā)布一些Swift的文章什么的,歡迎關(guān)注
