【面试必备】Swiftamp;nbsp;面试题及其答案
-
原文:Swift Interview Questions and Answers
-
原作者:Antonio Bello
-
原作者介紹: Antonio 擁有豐富的編程經(jīng)驗(yàn)。他開始編程的時(shí)候,內(nèi)存單位還是 byte 而不是 gigabyte,存儲(chǔ)空間還是可選的附加物,最常用的語(yǔ)言還是BASIC。現(xiàn)在,Antonio 的興趣在于 iOS 應(yīng)用開發(fā)、node.js 后端開發(fā),而且他從來(lái)不會(huì)放過(guò)學(xué)習(xí)新東西的機(jī)會(huì)。他認(rèn)為 Swift 是一門很有表現(xiàn)力的語(yǔ)言,不過(guò) Objective-C 仍然是偉大而不同尋常的。
-
譯者:本文由CocoaChina譯者 淺夏@舊時(shí)光 翻譯
雖然Swift出生才一年,但是它已經(jīng)成為最流行的編程語(yǔ)言之一了。它的語(yǔ)法很簡(jiǎn)單,以至于當(dāng)它發(fā)布的時(shí)候,JavaScript開發(fā)者感覺(jué)就像下圖一樣
事實(shí)上,Swift是一種復(fù)雜的語(yǔ)言。它包含面向?qū)ο蠛秃瘮?shù)方法這兩個(gè)方面,并且隨著新版本的發(fā)布在一直進(jìn)化。
Swift的知識(shí)浩如煙海——但是怎么測(cè)試你掌握了多少?在這篇文章中,我和這個(gè)raywenderlich.com網(wǎng)站的教學(xué)團(tuán)隊(duì)共同寫了一個(gè)Swift面試問(wèn)題列表。
你可以用這些問(wèn)題來(lái)測(cè)試應(yīng)聘者關(guān)于Swift方面的知識(shí)水平,或者測(cè)試一下你自己。如果你不知道答案,沒(méi)關(guān)系,沒(méi)一個(gè)問(wèn)題下面都有答案供你學(xué)習(xí)。
這些問(wèn)題包含兩個(gè)方面:
-
筆試問(wèn)題:通過(guò)電子郵件做一個(gè)編程測(cè)試是極好的,因?yàn)檫@涉及到寫大量的代碼,從代碼質(zhì)量上可以看出一個(gè)人的水平。
-
面試問(wèn)題:電話面試或者面對(duì)面面試也是很好的,因?yàn)閷?duì)面試者來(lái)說(shuō)口頭交流會(huì)更方面。
每個(gè)方面有分成三個(gè)等級(jí):
-
初級(jí):適合讀了一到兩本有關(guān)Swift的書,并且已經(jīng)開始用Swift開發(fā)應(yīng)用程序的初學(xué)者。
-
中級(jí):適合那些對(duì)Swift語(yǔ)言的概念有深刻理解和強(qiáng)烈興趣的,并且一直在閱讀大量有關(guān)Swift的博客文章并進(jìn)行實(shí)踐的中級(jí)工程師。
-
高級(jí):適合那些以探索Swift語(yǔ)言知識(shí)為樂(lè)趣,挑戰(zhàn)自己,使用前言技術(shù)的人們。
假如你想回答這些問(wèn)題,我建議你在回答這些問(wèn)題之前,打開Playground運(yùn)行一下這些問(wèn)題的代碼。這些問(wèn)題的答案都在Xcode 7.0 Beta 6 版本中測(cè)試過(guò)。
準(zhǔn)備好了嗎?系好安全帶,現(xiàn)在就開始!
編者注:特別感謝raywenderlich.com教學(xué)團(tuán)隊(duì)成員Warren Burton,?Greg Heo,?Mikael Konutgan,?Tim Mitra,?Luke Parham,?Rui Peres, 和?Ray Wenderlich?,他們幫我相處了下面問(wèn)題中得一些,并且測(cè)試區(qū)分難度級(jí)別。
筆試問(wèn)題
初學(xué)者
問(wèn)題1、(Swift 1.0及其之后的版本的問(wèn)題)有什么更好的方法來(lái)寫下面的for循環(huán)?
for?var?i?=?0;?i?<?5;?i++?{print("Hello!") }答案:
for?_?in?0...4?{print("Hello!") }Swift 實(shí)現(xiàn)了兩個(gè)數(shù)組運(yùn)算符closed operator 和 half-operator.前者包含數(shù)組中得所有值。例如:下面的例子包含從0到4得所有整數(shù):
0...4half-operator不包含數(shù)組中的最后一個(gè)元素,下面的例子會(huì)得到的結(jié)果和上面的一樣:
0..<5問(wèn)題2– Swift 1.0 or later
思考下面的問(wèn)題:
struct?Tutorial?{var?difficulty:?Int?=?1 }var?tutorial1?=?Tutorial() var?tutorial2?=?tutorial1 tutorial2.difficulty?=?2tutorial1.difficulty 和 tutorial2.difficulty的值分別是多少?假如Tutorial是一個(gè)類,會(huì)有什么不同?并說(shuō)明原因。
答案:tutorial1.difficulty ?的值是1,然而tutorial2.difficulty的值是2.
在Swift中結(jié)構(gòu)體是值類型,他們的值是復(fù)制的而不是引用的。下面的一行代碼意思是復(fù)制了tutorial1的值并把它賦值給tutorial2:
var?tutorial2?=?tutorial1從這一行開始,tutorial2值得改變并不影響tutorial1的值。
假如Tutorial是一個(gè)類,tutorial1.difficulty和tutorial2.difficulty的值將都會(huì)是2.在Swift中類對(duì)象都是引用類型。tutorial1屬性的任何改變將會(huì)反應(yīng)到tutorial2上,反之亦然。
問(wèn)題3 – Swift 1.0 or later
view1聲明成var類型,view2聲明let類型。這里有什么區(qū)別嗎?下面的最后一行代碼能編譯嗎?
import?UIKitvar?view1?=?UIView() view1.alpha?=?0.5let?view2?=?UIView() view2.alpha?=?0.5?//?Will?this?line?compile?答案:view1是個(gè)變量可以重新賦值給一個(gè)新的實(shí)例化的UIView對(duì)象。使用let你只賦值一次,所以下面的代碼是不能編譯的:
view2?=?view1?//?Error:?view2?is?immutable但是UIView是一個(gè)引用類型的類,所以你可以改變view2的屬性,也就是說(shuō)最后一行代碼是可以編譯的:
let?view2?=?UIView() view2.alpha?=?0.5?//?Yes!問(wèn)題4 – Swift 1.0 or later
下面的代碼是把數(shù)組里面的名字按字母的順序排序,看上去比較復(fù)雜。盡最大的可能簡(jiǎn)化閉包里的代碼。
let?animals?=?["fish",?"cat",?"chicken",?"dog"] let?sortedAnimals?=?animals.sort?{?(one:?String,?two:?String)?->?Bool?inreturn?one?<?two }答案:
第一個(gè)簡(jiǎn)化的是參數(shù)。系統(tǒng)的參數(shù)類型推斷功能,可以計(jì)算出閉包里面參數(shù)的類型,所以你不必定義參數(shù)的類型:
let?sortedAnimals?=?animals.sort?{?(one,?two)?->?Bool?in?return?one?<?two?}函數(shù)返回值也可以被推斷出來(lái),所以簡(jiǎn)化掉,代碼變?yōu)?#xff1a;
let?sortedAnimals?=?animals.sort?{?(one,?two)?in?return?one?<?two?}這個(gè)$i 符號(hào)可以代替參數(shù)名字,代碼進(jìn)一步簡(jiǎn)化為:
let?sortedAnimals?=?animals.sort?{?return?$0?<?$1?}在一個(gè)獨(dú)立的閉包內(nèi),return這個(gè)關(guān)鍵字是可以省略的。最后聲明的返回值就是閉包的返回值:
let?sortedAnimals?=?animals.sort?{?$0?<?$1?}這簡(jiǎn)化很多了,但是我們不能止步于此!
對(duì)于字符串,有一個(gè)定義如下的比較函數(shù):
func??Bool這個(gè)簡(jiǎn)單的小函數(shù)可以使你的代碼簡(jiǎn)潔如下:
let?sortedAnimals?=?animals.sort(<)注意每一步的編譯結(jié)果都相同,但是最后一步你的閉包里只有一個(gè)字符。
問(wèn)題5 – Swift 1.0 or later
下面的代碼創(chuàng)建了兩個(gè)類Address和Person,并且創(chuàng)建了兩個(gè)實(shí)例對(duì)象分別代表Ray和Brain.
class?Address?{var?fullAddress:?Stringvar?city:?Stringinit(fullAddress:?String,?city:?String)?{self.fullAddress?=?fullAddressself.city?=?city} }class?Person?{var?name:?Stringvar?address:?Addressinit(name:?String,?address:?Address)?{self.name?=?nameself.address?=?address} }var?headquarters?=?Address(fullAddress:?"123?Tutorial?Street",?city:?"Appletown") var?ray?=?Person(name:?"Ray",?address:?headquarters) var?brian?=?Person(name:?"Brian",?address:?headquarters)假設(shè)Brain搬家到街對(duì)面的建筑物里,那么你會(huì)這樣更新他的地址:
brian.address.fullAddress?=?"148?Tutorial?Street"這樣做將會(huì)發(fā)生什么?錯(cuò)誤出在什么地方呢?
答案:Ray同樣會(huì)搬家到新的建筑物里面。Address是一個(gè)引用類型類,所以無(wú)論你是通過(guò)ray或者brain訪問(wèn)headquarters,訪問(wèn)都是同一個(gè)實(shí)例化對(duì)象。headquarters對(duì)象的變化也會(huì)引起ray和brain的變化。你能想象如果Brain收到Ray的郵件或者相反Ray收到Brain的郵件,將會(huì)發(fā)生什么?解決方案是創(chuàng)建一個(gè)新的Address對(duì)象賦值給Brain或者把Address聲明成為結(jié)構(gòu)體而不是一個(gè)類。
中級(jí)
問(wèn)題1– Swift 2.0 or later
思考下面的代碼:
var?optional1:?String??=?nil var?optional2:?String??=?.Nonenil 和 .None有什么不同?optional1和optional2有什么不同?
答案:兩者沒(méi)有什么不同。Optional.None(簡(jiǎn)稱.None)是optional變量值初始化的標(biāo)準(zhǔn)方法,而nil只是.None語(yǔ)法的一種修飾。事實(shí)上下面語(yǔ)句輸出是正確的:
nil?==?.None?//?On?Swift?1.x?this?doesn't?compile.?You?need?Optional .None記住枚舉類型的Optional下的None:
enum?Optional{case?Nonecase?Some(T) }問(wèn)題2-Swift 1.0 or later
下面是thermometer作為類和結(jié)構(gòu)體的例子:
public?class?ThermometerClass?{private(set)?var?temperature:?Double?=?0.0public?func?registerTemperature(temperature:?Double)?{self.temperature?=?temperature} }let?thermometerClass?=?ThermometerClass() thermometerClass.registerTemperature(56.0)public?struct?ThermometerStruct?{private(set)?var?temperature:?Double?=?0.0public?mutating?func?registerTemperature(temperature:?Double)?{self.temperature?=?temperature} }let?thermometerStruct?=?ThermometerStruct() thermometerStruct.registerTemperature(56.0)但是這段代碼編譯失敗了,請(qǐng)問(wèn)哪里報(bào)錯(cuò),出錯(cuò)的原因是什么。
建議:在使用Playground之前,認(rèn)真閱讀代碼并思考。
答案:代碼的最后一行不會(huì)被編譯通過(guò)。ThermometerStruct結(jié)構(gòu)體中正確的聲明了一個(gè)mutating屬性函數(shù),它是用來(lái)改變結(jié)構(gòu)體內(nèi)部temperature屬性的值的,但是編譯器不通過(guò)的原因是,通過(guò)let創(chuàng)建的不可變的registerTemperature結(jié)構(gòu)體調(diào)用了registerTemperature函數(shù)。
問(wèn)題3– Swift 1.0 or later
下面的代碼輸出是什么?并說(shuō)明理由。
var?thing?=?"cars"let?closure?=?{?[thing]?inprint("I?love?\(thing)") }thing?=?"airplanes"closure()答案:輸出的是:I love cars。當(dāng)閉包被聲明的時(shí)候,抓捕列表就復(fù)制一份thing變量,所以被捕捉的值并沒(méi)有改變,即使你給thing賦了一個(gè)新值。
如果你要忽視閉包中捕捉列表的值,那么編譯器引用那個(gè)值而不是復(fù)制。這種情況下,被引用變量的值的變化將會(huì)反映到閉包中,正如下面的代碼所示:
var?thing?=?"cars"let?closure?=?{???print("I?love?\(thing)") }thing?=?"airplanes"closure()?//?Prints?"I?love?airplanes"問(wèn)題4– Swift 2.0 or later
下面是一個(gè)全局函數(shù),這個(gè)函數(shù)的功能是計(jì)算數(shù)組中特殊值得個(gè)數(shù)。(待校驗(yàn))
func?countUniques(array:?Array)?->?Int?{let?sorted?=?array.sort(<)let?initial:?(T?,?Int)?=?(.None,?0)let?reduced?=?sorted.reduce(initial)?{?($1,?$0.0?==?$1???$0.1?:?$0.1?+?1)?}return?reduced.1 }它使用了< 和==運(yùn)算符,他們限制著T(占位類型)的實(shí)際類型,也就是說(shuō)T必須遵循Comparable協(xié)議。你可以這樣使用它:
countUniques([1,?2,?3,?3])?//?result?is?3現(xiàn)在要求你重寫上面的方法作為Array的擴(kuò)展方法,然后你就可以這樣寫代碼:
[1,?2,?3,?3].countUniques()?//?should?print?3如何實(shí)現(xiàn)?
答案:在Swift 2.0 中,泛類型可以使用類型約束條件被強(qiáng)制擴(kuò)展。但是假如這個(gè)泛類型不滿足這個(gè)類型的約束條件,那么這個(gè)擴(kuò)展方法既不可見也無(wú)法調(diào)用。
所以countUniques全局函數(shù)可以作為Array的擴(kuò)展方法被重寫如下:
extension?Array?where?Element:?Comparable?{func?countUniques()?->?Int?{let?sorted?=?sort(<)let?initial:?(Element?,?Int)?=?(.None,?0)let?reduced?=?sorted.reduce(initial)?{?($1,?$0.0?==?$1???$0.1?:?$0.1?+?1)?}return?reduced.1} }注意:只有元類型實(shí)現(xiàn)了Comparable協(xié)議新的方法才可以被使用。例如,如果你在全部是UIView對(duì)象的數(shù)組中調(diào)用countUniques,編譯器將會(huì)報(bào)錯(cuò)。
import?UIKit let?a?=?[UIView(),?UIView()] a.countUniques()?//?compiler?error?here?because?UIView?doesn't?implement?Comparable問(wèn)題5- Swift 2.0 or later
下面一個(gè)函數(shù)的功能是計(jì)算兩個(gè)double(optional)類型的數(shù)的相除的結(jié)果。在執(zhí)行除法之前,必須提前滿足三個(gè)條件:
被除數(shù)必須包含nil值
除數(shù)必須為包含nil值
除數(shù)不能為零
func?divide(dividend:?Double?,?by?divisor:?Double?)?->?Double??{if?dividend?==?.None?{return?.None}if?divisor?==?.None?{return?.None}if?divisor?==?0?{return?.None}return?dividend!?/?divisor! }上面的函數(shù)可以正常使用,但是會(huì)存在兩個(gè)問(wèn)題:
那些前提條件可以利用guard語(yǔ)句。
使用了強(qiáng)制拆包。
請(qǐng)使用guard語(yǔ)句和避免使用強(qiáng)制拆包來(lái)優(yōu)化這個(gè)函數(shù)。
答案:guard語(yǔ)句是在Swift 2.0中引進(jìn)的,它是用途是在未滿足某個(gè)條件時(shí),提供一個(gè)退出的路徑。對(duì)于檢查是否滿足先決條件來(lái)說(shuō),它是非常有用的。因?yàn)樗梢允鼓愀逦谋磉_(dá)邏輯——而不是像i各種f語(yǔ)句嵌套實(shí)現(xiàn)那么復(fù)雜。下面就是一個(gè)例子:
guard?dividend?!=?.None?else?{?return?.None?}它也可以在optional binding(可選綁定)中使用。使用guard語(yǔ)句之后,使拆包后的變量可以被訪問(wèn)。
guard?let?dividend?=?dividend?else?{?return?.None?}所以divide函數(shù)被重寫如下:
func?divide(dividend:?Double?,?by?divisor:?Double?)?->?Double??{guard?let?dividend?=?dividend?else?{?return?.None?}guard?let?divisor?=?divisor?else?{?return?.None?}guard?divisor?!=?0?else?{?return?.None?}return?dividend?/?divisor }我們發(fā)現(xiàn)隱身的可拆包的運(yùn)算在代碼的最后一行,因?yàn)閐ividend和divisor這兩個(gè)參數(shù)已經(jīng)被拆包并且以分別以一個(gè)常量來(lái)存儲(chǔ)。
因此,你可以再次使用guard語(yǔ)句,使上面的函數(shù)更簡(jiǎn)潔:
func?divide(dividend:?Double?,?by?divisor:?Double?)?->?Double??{guard?let?dividend?=?dividend,?divisor?=?divisor?where?divisor?!=?0?else?{?return?.None?}return?dividend?/?divisor }上面的函數(shù)中使用了兩個(gè)guard語(yǔ)句,因?yàn)槭褂昧藈here語(yǔ)句指定了divisor不能為0的條件。
高級(jí)
問(wèn)題1- Swift 1.0 or later
下面是thermometer作為結(jié)構(gòu)體的例子:
public?struct?Thermometer?{public?var?temperature:?Doublepublic?init(temperature:?Double)?{self.temperature?=?temperature} }創(chuàng)建一個(gè)thermometer實(shí)例對(duì)象,你可以使用下面的代碼:
var?t:?Thermometer?=?Thermometer(temperature:56.8)但是像下面的代碼那樣初始化對(duì)象是否會(huì)更好:
var?thermometer:?Thermometer?=?56.8能這樣做嗎?如果可以,怎么做?提示:it has to do with convertibles, but not convertibles like Camaros and Mustangs
答案:Swift 定義了下面的協(xié)議,這些協(xié)議可以使一種類型通過(guò)字面量的方式來(lái)初始化并賦值。
NilLiteralConvertible BooleanLiteralConvertible IntegerLiteralConvertible FloatLiteralConvertible UnicodeScalarLiteralConvertible ExtendedGraphemeClusterLiteralConvertible StringLiteralConvertible ArrayLiteralConvertible DictionaryLiteralConvertible采用相應(yīng)的協(xié)議并且提供一個(gè)允許字面量初始化的公用方法。在Thermometer類型的例子下,我們需要實(shí)現(xiàn)FloatLiteralConvertible協(xié)議,代碼如下:
extension?Thermometer?:?FloatLiteralConvertible?{public?init(floatLiteral?value:?FloatLiteralType)?{self.init(temperature:?value)} }那么現(xiàn)在,你就可以通過(guò)一個(gè)簡(jiǎn)單的float數(shù)字創(chuàng)建一Thermometer對(duì)象,代碼如下:
var?thermometer:?Thermometer?=?56.8問(wèn)題2 - Swift 1.0 or later
Swift 擁有一系列預(yù)定義的運(yùn)算符,這些運(yùn)算符執(zhí)行不同類型的操作,例如算術(shù)運(yùn)算符和邏輯運(yùn)算符。它甚至允許創(chuàng)建自定義的運(yùn)算符,無(wú)論是一元運(yùn)算符還是二元運(yùn)算符。自定義一個(gè)滿足一下規(guī)格的冪運(yùn)算符:
以兩個(gè)整數(shù)作為參數(shù)
返回第一個(gè)參數(shù)的第二個(gè)參數(shù)次方的值
忽略潛在溢出錯(cuò)誤
答案:創(chuàng)建一個(gè)自定義的運(yùn)算符需要兩個(gè)步驟:聲明它和實(shí)現(xiàn)它。
使用operator關(guān)鍵字來(lái)聲明指定的類型(一元或者二元)、組成這個(gè)運(yùn)算符字符的順序已經(jīng)它的優(yōu)先級(jí)和關(guān)聯(lián)性。
在這中情況下,運(yùn)算符是^^,類型是infix(二進(jìn)制),關(guān)聯(lián)性是right,優(yōu)先級(jí)設(shè)置成為155,原因是乘法和除法的優(yōu)先級(jí)是150.下面就是具體的聲明代碼:
infix?operator?^^?{?associativity?right?precedence?155?}代碼實(shí)現(xiàn)如下:
func?^^(lhs:?Int,?rhs:?Int)?->?Int?{let?l?=?Double(lhs)let?r?=?Double(rhs)let?p?=?pow(l,?r)return?Int(p) }值得注意的是,它并不需要溢出考慮;如果操作產(chǎn)生的結(jié)果int不能代表,如大于int.max,就會(huì)發(fā)生運(yùn)行時(shí)錯(cuò)誤。
問(wèn)題3 - Swift 1.0 or later
你能像下面的代碼一樣使用原始值定義一個(gè)枚舉類型嗎?如果不行,說(shuō)明原因。
enum?Edges?:?(Double,?Double)?{case?TopLeft?=?(0.0,?0.0)case?TopRight?=?(1.0,?0.0)case?BottomLeft?=?(0.0,?1.0)case?BottomRight?=?(1.0,?1.0) }答案:不行。原始值得類型必須滿足一下條件
遵守Equatable協(xié)議
滿足能轉(zhuǎn)換成下列類型中的任何一個(gè)類型: ? ?
a.Intb.Stringc.?Character在上面的代碼中,原始值即使是獨(dú)立的個(gè)體值,但是它仍然是一個(gè)不兼容的元組。?
問(wèn)題4- Swift 2.0 or later
下面的代碼定義了一個(gè)結(jié)構(gòu)體Pizza和一個(gè)協(xié)議Pizzeria,這個(gè)協(xié)議有一個(gè)包含makeMargherita()函數(shù)的擴(kuò)展。
struct?Pizza?{let?ingredients:?[String] }protocol?Pizzeria?{func?makePizza(ingredients:?[String])?->?Pizzafunc?makeMargherita()?->?Pizza }extension?Pizzeria?{func?makeMargherita()?->?Pizza?{return?makePizza(["tomato",?"mozzarella"])} }現(xiàn)在你將要定義一個(gè)如下的Lombardi的餐館:
struct?Lombardis:?Pizzeria?{func?makePizza(ingredients:?[String])?->?Pizza?{return?Pizza(ingredients:?ingredients)}func?makeMargherita()?->?Pizza?{return?makePizza(["tomato",?"basil",?"mozzarella"])} }下面的代碼創(chuàng)建了Lombardis類型的兩個(gè)實(shí)例對(duì)象,哪一個(gè)對(duì)象會(huì)產(chǎn)生一個(gè)帶有basil的margherita披薩?
let?lombardis1:?Pizzeria?=?Lombardis() let?lombardis2:?Lombardis?=?Lombardis()lombardis1.makeMargherita() lombardis2.makeMargherita()答案:兩個(gè)都可以。Pizzeria協(xié)議聲明了makeMargherita()方法并且提供了一個(gè)默認(rèn)的實(shí)現(xiàn),而且它又在Lombardis的實(shí)現(xiàn)中被重寫。在這兩種情況下,由于這個(gè)方法在協(xié)議中被聲明,那么在運(yùn)行時(shí)相應(yīng)的實(shí)現(xiàn)就會(huì)被調(diào)用。
假如Pizzeria協(xié)議沒(méi)有聲明makeMargherita()方法,但是擴(kuò)展中仍然提供了如下的代碼的這個(gè)方法默認(rèn)的實(shí)現(xiàn),會(huì)發(fā)生什么?
protocol?Pizzeria?{func?makePizza(ingredients:?[String])?->?Pizza }extension?Pizzeria?{func?makeMargherita()?->?Pizza?{return?makePizza(["tomato",?"mozzarella"])} }這種情況下,只有l(wèi)ombardis2會(huì)產(chǎn)生一個(gè)帶有basil的pizza,而lombardis1將會(huì)產(chǎn)生一個(gè)不帶basil的pizza,原因是它會(huì)調(diào)用擴(kuò)展中定義的那個(gè)方法。
問(wèn)題5- Swift 2.0 or later
下面的代碼有一個(gè)編譯錯(cuò)誤,請(qǐng)指出來(lái)并說(shuō)明原因。
struct?Kitten?{ }func?showKitten(kitten:?Kitten?)?{guard?let?k?=?kitten?else?{print("There?is?no?kitten")}print(k) }提示:有三種方法修復(fù)它。
答案:guard語(yǔ)句中得else語(yǔ)句必須需要一個(gè)返回路徑,你可以使用return ,拋出異常或者調(diào)用@noreturn.最簡(jiǎn)單的解決方法是添加一個(gè)return語(yǔ)句,代碼如下:
func?showKitten(kitten:?Kitten?)?{guard?let?k?=?kitten?else?{print("There?is?no?kitten")return}print(k) }下面是拋出異常的版本:
enum?KittenError:?ErrorType?{case?NoKitten }struct?Kitten?{ }func?showKitten(kitten:?Kitten?)?throws?{guard?let?k?=?kitten?else?{print("There?is?no?kitten")throw?KittenError.NoKitten}print(k) }try?showKitten(nil)最后,調(diào)用一個(gè)@noreturn功能函數(shù) fatalError( ) 解決方案:
struct?Kitten?{ }func?showKitten(kitten:?Kitten?)?{guard?let?k?=?kitten?else?{print("There?is?no?kitten")fatalError()}print(k) }面試試題
你很棒,但是你還沒(méi)達(dá)到絕地武士的要求。任何人都可以寫出代碼,但是你如何處理理論和實(shí)踐相結(jié)合的開放式問(wèn)題?
為了解決這些問(wèn)題,你仍然需要使用Playgroud寫代碼來(lái)驗(yàn)證一些問(wèn)題。
初級(jí)
問(wèn)題1- Swift 1.0 or later
什么是optional類型,它是用來(lái)解決什么問(wèn)題的?
答案:optional類型被用來(lái)表示任何類型的變量都可以表示缺少值。在Objective-C中,引用類型的變量是可以缺少值得,并且使用nil作為缺少值。基本的數(shù)據(jù)類型如int 或者float沒(méi)有這種功能。
Swift用optional擴(kuò)展了在基本數(shù)據(jù)類型和引用類型中缺少值的概念。一個(gè)optional類型的變量,在任何時(shí)候都可以保存一個(gè)值或者為nil。
問(wèn)題2- Swift 1.0 or later
在Swfit中,什么時(shí)候用結(jié)構(gòu)體,什么時(shí)候用類?
答案:一直都有這樣的爭(zhēng)論:到底是用類的做法優(yōu)于用結(jié)構(gòu)體,還是用結(jié)構(gòu)體的做法優(yōu)于類。函數(shù)式編程傾向于值類型,面向?qū)ο缶幊谈矚g類。
在Swift 中,類和結(jié)構(gòu)體有許多不同的特性。下面是兩者不同的總結(jié):
類支持繼承,結(jié)構(gòu)體不支持。
類是引用類型,結(jié)構(gòu)體是值類型
并沒(méi)有通用的規(guī)則決定結(jié)構(gòu)體和類哪一個(gè)更好用。一般的建議是使用最小的工具來(lái)完成你的目標(biāo),但是有一個(gè)好的經(jīng)驗(yàn)是多使用結(jié)構(gòu)體,除非你用了繼承和引用語(yǔ)義。
想要了解更多,點(diǎn)擊這里。
注意:在運(yùn)行時(shí),結(jié)構(gòu)體的在性能方面更優(yōu)于類,原因是結(jié)構(gòu)體的方法調(diào)用是靜態(tài)綁定,而類的方法調(diào)用是動(dòng)態(tài)實(shí)現(xiàn)的。這就是盡可能得使用結(jié)構(gòu)體代替類的又一個(gè)好的原因。
問(wèn)題3- Swift 1.0 or later
什么是泛型?泛型是用來(lái)解決什么問(wèn)題的?
答案:泛型是用來(lái)使類型和算法安全的工作的一種類型。在Swift中,在函數(shù)和數(shù)據(jù)結(jié)構(gòu)中都可以使用泛型,例如類、結(jié)構(gòu)體和枚舉。
泛型一般是用來(lái)解決代碼復(fù)用的問(wèn)題。常見的一種情況是,你有一個(gè)函數(shù),它帶有一個(gè)參數(shù),參數(shù)類型是A,然而當(dāng)參數(shù)類型改變成B的時(shí)候,你不得不復(fù)制這個(gè)函數(shù)。
例如,下面的代碼中第二個(gè)函數(shù)就是復(fù)制第一個(gè)函數(shù)——它僅僅是用String類型代替了Integer類型。
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)?//?trueObjective-C開發(fā)人員可能想到用NSObject類來(lái)解決這個(gè)問(wèn)題,代碼如下:
import?Foundationfunc?areTheyEqual(x:?NSObject,?_?y:?NSObject)?->?Bool?{return?x?==?y }areTheyEqual("ray",?"ray")?//?true areTheyEqual(1,?1)?//?true這個(gè)代碼會(huì)按照預(yù)期的方式工作,但是它在編譯時(shí)不安全。它允許字符串和整數(shù)相比較,像這樣:
areTheyEqual(1,?"ray")應(yīng)用程序不會(huì)崩潰,但是允許字符串和整數(shù)相比較可能不是預(yù)想的結(jié)果。
通過(guò)采用泛型,可以合并這兩個(gè)函數(shù)為一個(gè)并同時(shí)保持類型安全。下面是代碼實(shí)現(xiàn):
func?areTheyEqual(x:?T,?_?y:?T)?->?Bool?{return?x?==?y }areTheyEqual("ray",?"ray") areTheyEqual(1,?1)上面的例子是測(cè)試兩個(gè)參數(shù)是否相等,這兩個(gè)參數(shù)的類型受到約束都必須遵循Equatable協(xié)議。上面的代碼達(dá)到預(yù)想的結(jié)果,并且防止了傳遞不同類型的參數(shù)。
問(wèn)題4- Swift 1.0 or later
哪些情況下你不得不使用隱式拆包?說(shuō)明原因。
答案:對(duì)optional變量使用隱式拆包最常見的原因如下:
1、對(duì)象屬性在初始化的時(shí)候不能nil,否則不能被初始化。典型的例子是Interface Builder outlet類型的屬性,它總是在它的擁有者初始化之后再初始化。在這種特定的情況下,假設(shè)它在Interface Builder中被正確的配置——outlet被使用之前,保證它不為nil。
2、解決強(qiáng)引用的循環(huán)問(wèn)題——當(dāng)兩個(gè)實(shí)例對(duì)象相互引用,并且對(duì)引用的實(shí)例對(duì)象的值要求不能為nil時(shí)候。在這種情況下,引用的一方可以標(biāo)記為unowned,另一方使用隱式拆包。
建議:除非必要,不要對(duì)option類型使用隱式拆包。使用不當(dāng)會(huì)增加運(yùn)行時(shí)崩潰的可能性。在某些情況下,崩潰可能是有意的行為,但有更好的方法來(lái)達(dá)到相同的結(jié)果,例如,通過(guò)使用fatalError( )函數(shù)。
問(wèn)題5- Swift 1.0 or later
對(duì)一個(gè)optional變量拆包有多少種方法?并在安全方面進(jìn)行評(píng)價(jià)。
答案: ?
-
強(qiáng)制拆包 !操作符——不安全
-
隱式拆包變量聲明——大多數(shù)情況下不安全
-
可選綁定——安全
-
自判斷鏈接(optional chaining)——安全
-
nil coalescing 運(yùn)算符(空值合并運(yùn)算符)——安全
-
Swift 2.0 的新特性 guard 語(yǔ)句——安全
-
Swift 2.0 的新特性optional pattern(可選模式) ——安全(@Kametrixom支持)
?
中級(jí)
問(wèn)題1- Swift 1.0 or later
Swift 是面向?qū)ο缶幊陶Z(yǔ)言還是函數(shù)式編程語(yǔ)言?
答案:Swift是一種混合編程語(yǔ)言,它包含這兩種編程模式。它實(shí)現(xiàn)了面向?qū)ο蟮娜齻€(gè)基本原則:
-
封裝
-
繼承
-
多態(tài)
說(shuō)道Swift作為一種函數(shù)式編程語(yǔ)言,我們就不得不說(shuō)一下什么是函數(shù)式編程。有很多不同的方法去定義函數(shù)式編程語(yǔ)言,但是他們表達(dá)的意義相同。
最常見的定義來(lái)自維基百科:...它是一種編程規(guī)范…它把電腦運(yùn)算當(dāng)做數(shù)學(xué)函數(shù)計(jì)算,避免狀態(tài)改變和數(shù)據(jù)改變。
很難說(shuō)Swift是一個(gè)成熟的函數(shù)式語(yǔ)言,但是它已經(jīng)具備了函數(shù)式語(yǔ)言的基礎(chǔ)。
問(wèn)題2- Swift 1.0 or later
下面的功能特性都包含在Swift中嗎?
1、泛型類
2、泛型結(jié)構(gòu)體
3、泛型協(xié)議
答案:
-
Swift 包含1和2特性。泛型可以在類、結(jié)構(gòu)體、枚舉、全局函數(shù)或者方法中使用。
-
3是通過(guò)typealias部分實(shí)現(xiàn)的。typealias不是一個(gè)泛型類型,它只是一個(gè)占位符的名字。它通常是作為關(guān)聯(lián)類型被引用,只有協(xié)議被一個(gè)類型引用的時(shí)候它才被定義。
問(wèn)題3- Swift 1.0 or later
在Objective-C中,一個(gè)常量可以這樣定義:
const?int?number?=?0;類似的Swift是這樣定義的:
let?number?=?0兩者之間有什么不同嗎?如果有,請(qǐng)說(shuō)明原因。
答案:const常量是一個(gè)在編譯時(shí)或者編譯解析時(shí)被初始化的變量。通過(guò)let創(chuàng)建的是一個(gè)運(yùn)行時(shí)常量,是不可變得。它可以使用stattic 或者dynamic關(guān)鍵字來(lái)初始化。謹(jǐn)記它的的值只能被分配一次。
問(wèn)題4- Swift 1.0 or later
聲明一個(gè)靜態(tài)屬性或者函數(shù),我們常常使用值類型的static修飾符。下面就是一個(gè)結(jié)構(gòu)體的例子:
struct?Sun?{static?func?illuminate()?{} }對(duì)類來(lái)說(shuō),使用static 或者class修飾符,都是可以的。它們使用后的效果是一樣的,但是本質(zhì)上是不同的。能解釋一下為什么不同嗎?
答案:
static修飾的屬性或者修飾的函數(shù)都不可以重寫。但是使用class修飾符,你可以重寫屬性或者函數(shù)。
當(dāng)static在類中應(yīng)用的時(shí)候,static就成為class final的一個(gè)別名。
例如,在下面的代碼中,當(dāng)你嘗試重寫illuminate()函數(shù)時(shí),編譯器就會(huì)報(bào)錯(cuò):
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?methodsuper.illuminate()} }問(wèn)題5- Swift 1.0 or later
你能通過(guò)extension(擴(kuò)展)保存一個(gè)屬性嗎?請(qǐng)解釋一下原因。
答案:不能。擴(kuò)展可以給當(dāng)前的類型添加新的行為,但是不能改變本身的類型或者本身的接口。如果你添加一個(gè)新的可存儲(chǔ)的屬性,你需要額外的內(nèi)存來(lái)存儲(chǔ)新的值。擴(kuò)展并不能實(shí)現(xiàn)這樣的任務(wù)。
高級(jí)
問(wèn)題1- Swift 1.2
在Swift1.2版本中,你能解釋一下用泛型來(lái)聲明枚舉的問(wèn)題嗎?拿下面代碼中Either枚舉來(lái)舉例說(shuō)明吧,它有兩個(gè)泛型類型的參數(shù)T和V,參數(shù)T在關(guān)聯(lián)值類型為left情況下使用,參數(shù)V在關(guān)聯(lián)值為rihgt情況下使用,代碼如下:
enum?Either{case?Left(T)case?Right(V) }提示:驗(yàn)證上面的條件,需要在Xcode工程里面,而不是在Playgroud中。同時(shí)注意,這個(gè)問(wèn)題跟Swift1.2相關(guān),所以Xcode的版本必須是6.4以上。
答案:上面的代碼會(huì)出現(xiàn)編譯錯(cuò)誤:
unimplemented?IR?generation?feature?non-fixed?multi-payload?enum?layout問(wèn)題是T的內(nèi)存大小不能確定前期,因?yàn)樗蕾囉赥類型本身,但enum情況下需要一個(gè)固定大小的有效載荷。
最常用的解決方法是講泛類型用引用類型包裝起來(lái),通常稱為box,代碼如下:
class?Box{let?value:?Tinit(_?value:?T)?{self.value?=?value} }enum?Either{case?Left(Box)case?Right(Box) }這個(gè)問(wèn)題在Swift1.0及之后的版本出現(xiàn),但是Swift2.0的時(shí)候,被解決了。
問(wèn)題2- Swift 1.0 or later
閉包是引用類型嗎?
答案:閉包是引用類型。如果一個(gè)閉包被分配給一個(gè)變量,這個(gè)變量復(fù)制給另一個(gè)變量,那么他們引用的是同一個(gè)閉包,他們的捕捉列表也會(huì)被復(fù)制。
問(wèn)題3- Swift 1.0 or later
UInt類型是用來(lái)存儲(chǔ)無(wú)符號(hào)整型的。下面的代碼實(shí)現(xiàn)了一個(gè)有符號(hào)整型轉(zhuǎn)換的初始化方法:
init(_ value: Int)
然而,在下面的代碼中,當(dāng)你給一個(gè)負(fù)值的時(shí)候,它會(huì)產(chǎn)生一個(gè)編譯時(shí)錯(cuò)誤:
let myNegative = UInt(-1)
我們知道負(fù)數(shù)的內(nèi)部結(jié)構(gòu)是使用二進(jìn)制補(bǔ)碼的正數(shù),在保持這個(gè)負(fù)數(shù)內(nèi)存地址不變的情況下,如何把一個(gè)負(fù)整數(shù)轉(zhuǎn)換成一個(gè)無(wú)符號(hào)的整數(shù)?
答案:使用下面的初始化方法:
UInt(bitPattern: Int)
問(wèn)題4- Swift 1.0 or later
描述一種在Swift中出現(xiàn)循環(huán)引用的情況,并說(shuō)明怎么解決。
答案:循環(huán)引用出現(xiàn)在當(dāng)兩個(gè)實(shí)例對(duì)象相互擁有強(qiáng)引用關(guān)系的時(shí)候,這會(huì)造成內(nèi)存泄露,原因是這兩個(gè)對(duì)像都不會(huì)被釋放。只要一個(gè)對(duì)象被另一個(gè)對(duì)象強(qiáng)引用,那么該對(duì)象就不能被釋放,由于強(qiáng)引用的存在,每個(gè)對(duì)象都會(huì)保持對(duì)方存在。
解決這個(gè)問(wèn)題的方法是,用weak或者unowned引用代替其中一個(gè)的強(qiáng)引用,來(lái)打破循環(huán)引用。
問(wèn)題5- Swift 2.0 or later
Swift2.0 增加了一個(gè)新的關(guān)鍵字來(lái)實(shí)現(xiàn)遞歸枚舉。下面的例子是一個(gè)枚舉類型,它在Node條件下有兩個(gè)相關(guān)聯(lián)的值類型T和List:
enum?List{case?Node(T,?List) }什么關(guān)鍵字可以實(shí)現(xiàn)遞歸枚舉?
答案:indirect 關(guān)鍵值可以允許遞歸枚舉,代碼如下:
enum?List{indirect?case?Cons(T,?List) }Where To Go From Here?
恭喜你到了文章的最后,如果你不知道所有問(wèn)題的答案,也不要感到沮喪。
因?yàn)樯厦嬷械糜行﹩?wèn)題還是比較復(fù)雜的,并且Swift是一門富有表現(xiàn)力的語(yǔ)言,還有很多需要我們學(xué)。此外,蘋果公司一直改善Swift的新特性,所以即使學(xué)的最好的人也不可能知道所有的一切。
在你現(xiàn)有的Swift基礎(chǔ)知識(shí)之上,要深入了解Swift ,你就的看看Swift by Tutorials這本書,或者加入我們實(shí)踐教學(xué)協(xié)會(huì)RWDevCon。
當(dāng)然,關(guān)于Swift所有方面的資源都來(lái)是蘋果公司官方文檔The Swift Programming Language。
事實(shí)上,學(xué)習(xí)一門語(yǔ)言最好的方式是用它。在你的工程里或者Plaugroud里面使用Swift編程。Swift幾乎可以無(wú)縫銜接Object-C,所以在你現(xiàn)有的工程中使用Swift是一個(gè)學(xué)習(xí)Swift的很好的方法。
謝謝你的訪問(wèn)和回答這些問(wèn)題。在下面你可以隨意提問(wèn)交流。我也不介意你在下面貼上自己遇見的難題和挑戰(zhàn),我們可以相互學(xué)習(xí)。
溫馨提示:未經(jīng)博主許可禁止轉(zhuǎn)載!
總結(jié)
以上是生活随笔為你收集整理的【面试必备】Swiftamp;nbsp;面试题及其答案的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: ASP.NET MVC开发,编辑页面和添
- 下一篇: PHP验证码无法显示的原因