亚洲v欧美v国产v在线成_制服丝袜中文字幕丝袜专区_一区二区三区韩国电影_激情欧美一区二区中文字幕

我要投稿 投訴建議

Swift 面試題及其答案

時間:2022-10-22 03:11:10 面試試題 我要投稿
  • 相關(guān)推薦

Swift 面試題及其答案

  雖然Swift出生才一年,但是它已經(jīng)成為最流行的編程語言之一了。它的語法很簡單,以至于當它發(fā)布的時候,JavaScript開發(fā)者感覺就像下圖一樣。

Swift 面試題及其答案

  事實上,Swift是一種復雜的語言。它包含面向?qū)ο蠛秃瘮?shù)方法這兩個方面,并且隨著新版本的發(fā)布在一直進化。

  你可以用這些問題來測試應聘者關(guān)于Swift方面的知識水平,或者測試一下你自己。如果你不知道答案,沒關(guān)系,沒一個問題下面都有答案供你學習。

  這些問題包含兩個方面:

  筆試問題:通過電子郵件做一個編程測試是極好的,因為這涉及到寫大量的代碼,從代碼質(zhì)量上可以看出一個人的水平。

  面試問題:電話面試或者面對面面試也是很好的,因為對面試者來說口頭交流會更方面。

  每個方面有分成三個等級:

  初級:適合讀了一到兩本有關(guān)Swift的書,并且已經(jīng)開始用Swift開發(fā)應用程序的初學者。

  中級:適合那些對Swift語言的概念有深刻理解和強烈興趣的,并且一直在閱讀大量有關(guān)Swift的博客文章并進行實踐的中級工程師。

  高級:適合那些以探索Swift語言知識為樂趣,挑戰(zhàn)自己,使用前言技術(shù)的人們。

  假如你想回答這些問題,我建議你在回答這些問題之前,打開Playground運行一下這些問題的代碼。這些問題的答案都在Xcode 7.0 Beta 6 版本中測試過。

  準備好了嗎?系好安全帶,現(xiàn)在就開始!

  筆試問題

  初學者

  問題1、(Swift 1.0及其之后的版本的問題)有什么更好的方法來寫下面的for循環(huán)?

  for var i = 0; i < 5; i++ {

  print("Hello!")

  }

  答案:

  for _ in 0...4 {

  print("Hello!")

  }

  Swift 實現(xiàn)了兩個數(shù)組運算符closed operator 和 half-operator.前者包含數(shù)組中得所有值。例如:下面的例子包含從0到4得所有整數(shù)

  0...4

  half-operator不包含數(shù)組中的最后一個元素,下面的例子會得到的結(jié)果和上面的一樣:

  0..<5

  問題2– Swift 1.0 or later

  思考下面的問題:

  struct Tutorial {

  var difficulty: Int = 1

  }

  var tutorial1 = Tutorial()

  var tutorial2 = tutorial1

  tutorial2.difficulty = 2

  tutorial1.difficulty 和 tutorial2.difficulty的值分別是多少?假如Tutorial是一個類,會有什么不同?并說明原因。

  答案:tutorial1.difficulty 的值是1,然而tutorial2.difficulty的值是2.

  在Swift中結(jié)構(gòu)體是值類型,他們的值是復制的而不是引用的。下面的一行代碼意思是復制了tutorial1的值并把它賦值給tutorial2:

  var tutorial2 = tutorial1

  從這一行開始,tutorial2值得改變并不影響tutorial1的值。

  假如Tutorial是一個類,tutorial1.difficulty和tutorial2.difficulty的值將都會是2.在Swift中類對象都是引用類型。tutorial1屬性的任何改變將會反應到tutorial2上,反之亦然。

  問題3 – Swift 1.0 or later

  view1聲明成var類型,view2聲明let類型。這里有什么區(qū)別嗎?下面的最后一行代碼能編譯嗎?

  import UIKit

  var view1 = UIView()

  view1.alpha = 0.5

  let view2 = UIView()

  view2.alpha = 0.5 // Will this line compile?

  答案:view1是個變量可以重新賦值給一個新的實例化的UIView對象。使用let你只賦值一次,所以下面的代碼是不能編譯的:

  view2 = view1 // Error: view2 is immutable

  但是UIView是一個引用類型的類,所以你可以改變view2的屬性,也就是說最后一行代碼是可以編譯的:

  let view2 = UIView()

  view2.alpha = 0.5 // Yes!

  問題4 – Swift 1.0 or later

  下面的代碼是把數(shù)組里面的名字按字母的順序排序,看上去比較復雜。盡最大的可能簡化閉包里的代碼。

  let animals = ["fish", "cat", "chicken", "dog"]

  let sortedAnimals = animals.sort { (one: String, two: String) -> Bool in

  return one < two

  }

  答案:

  第一個簡化的是參數(shù)。系統(tǒng)的參數(shù)類型推斷功能,可以計算出閉包里面參數(shù)的類型,所以你不必定義參數(shù)的類型:

  let sortedAnimals = animals.sort { (one, two) -> Bool in return one < two }

  函數(shù)返回值也可以被推斷出來,所以簡化掉,代碼變?yōu)椋?/p>

  let sortedAnimals = animals.sort { (one, two) in return one < two }

  這個$i 符號可以代替參數(shù)名字,代碼進一步簡化為:

  let sortedAnimals = animals.sort { return $0 < $1 }

  在一個獨立的閉包內(nèi),return這個關(guān)鍵字是可以省略的。最后聲明的返回值就是閉包的返回值:

  let sortedAnimals = animals.sort { $0 < $1 }

  這簡化很多了,但是我們不能止步于此!

  對于字符串,有一個定義如下的比較函數(shù):

  func Bool

  這個簡單的小函數(shù)可以使你的代碼簡潔如下:

  let sortedAnimals = animals.sort(<)

  注意每一步的編譯結(jié)果都相同,但是最后一步你的閉包里只有一個字符。

  問題5 – Swift 1.0 or later

  下面的代碼創(chuàng)建了兩個類Address和Person,并且創(chuàng)建了兩個實例對象分別代表Ray和Brain.

  class Address {

  var fullAddress: String

  var city: String

  init(fullAddress: String, city: String) {

  self.fullAddress = fullAddress

  self.city = city

  }

  }

  class Person {

  var name: String

  var address: Address

  init(name: String, address: Address) {

  self.name = name

  self.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)

  假設Brain搬家到街對面的建筑物里,那么你會這樣更新他的地址:

  brian.address.fullAddress = "148 Tutorial Street"

  這樣做將會發(fā)生什么?錯誤出在什么地方呢?

  答案:Ray同樣會搬家到新的建筑物里面。Address是一個引用類型類,所以無論你是通過ray或者brain訪問headquarters,訪問都是同一個實例化對象。headquarters對象的變化也會引起ray和brain的變化。你能想象如果Brain收到Ray的郵件或者相反Ray收到Brain的郵件,將會發(fā)生什么?解決方案是創(chuàng)建一個新的Address對象賦值給Brain或者把Address聲明成為結(jié)構(gòu)體而不是一個類。

  中級

  問題1– Swift 2.0 or later

  思考下面的代碼:

  var optional1: String? = nil

  var optional2: String? = .None

  答案:兩者沒有什么不同。Optional.None(簡稱.None)是optional變量值初始化的標準方法,而nil只是.None語法的一種修飾。事實上下面語句輸出是正確的:

  nil == .None // On Swift 1.x this doesn't compile. You need Optional

  .None

  記住枚舉類型的Optional下的None:

  enum Optional{

  case None

  case Some(T)

  }

  問題2-Swift 1.0 or later

  下面是thermometer作為類和結(jié)構(gòu)體的例子:

  public class ThermometerClass {

  private(set) var temperature: Double = 0.0

  public func registerTemperature(temperature: Double) {

  self.temperature = temperature

  }

  }

  let thermometerClass = ThermometerClass()

  thermometerClass.registerTemperature(56.0)

  public struct ThermometerStruct {

  private(set) var temperature: Double = 0.0

  public mutating func registerTemperature(temperature: Double) {

  self.temperature = temperature

  }

  }

  let thermometerStruct = ThermometerStruct()

  thermometerStruct.registerTemperature(56.0)

  但是這段代碼編譯失敗了,請問哪里報錯,出錯的原因是什么。

  建議:在使用Playground之前,認真閱讀代碼并思考。

  答案:代碼的最后一行不會被編譯通過。ThermometerStruct結(jié)構(gòu)體中正確的聲明了一個mutating屬性函數(shù),它是用來改變結(jié)構(gòu)體內(nèi)部temperature屬性的值的,但是編譯器不通過的原因是,通過let創(chuàng)建的不可變的registerTemperature結(jié)構(gòu)體調(diào)用了registerTemperature函數(shù)。

  問題3– Swift 1.0 or later

  下面的代碼輸出是什么?并說明理由。

  var thing = "cars"

  let closure = { [thing] in

  print("I love \(thing)")

  }

  thing = "airplanes"

  closure()

  答案:輸出的是:I love cars。當閉包被聲明的時候,抓捕列表就復制一份thing變量,所以被捕捉的值并沒有改變,即使你給thing賦了一個新值。

  如果你要忽視閉包中捕捉列表的值,那么編譯器引用那個值而不是復制。這種情況下,被引用變量的值的變化將會反映到閉包中,正如下面的代碼所示:

  var thing = "cars"

  let closure = {

  print("I love \(thing)")

  }

  thing = "airplanes"

  closure() // Prints "I love airplanes"

  問題4– Swift 2.0 or later

  下面是一個全局函數(shù),這個函數(shù)的功能是計算數(shù)組中特殊值得個數(shù)。(待校驗)

  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

  }

  它使用了< 和==運算符,他們限制著T(占位類型)的實際類型,也就是說T必須遵循Comparable協(xié)議。你可以這樣使用它:

  countUniques([1, 2, 3, 3]) // result is 3

  現(xiàn)在要求你重寫上面的方法作為Array的擴展方法,然后你就可以這樣寫代碼:

  [1, 2, 3, 3].countUniques() // should print 3

  如何實現(xiàn)?

  答案:在Swift 2.0 中,泛類型可以使用類型約束條件被強制擴展。但是假如這個泛類型不滿足這個類型的約束條件,那么這個擴展方法既不可見也無法調(diào)用。

  所以countUniques全局函數(shù)可以作為Array的擴展方法被重寫如下:

  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

  }

  }

  注意:只有元類型實現(xiàn)了Comparable協(xié)議新的方法才可以被使用。例如,如果你在全部是UIView對象的數(shù)組中調(diào)用countUniques,編譯器將會報錯。

  import UIKit

  let a = [UIView(), UIView()]

  a.countUniques() // compiler error here because UIView doesn't implement Comparable

  問題5- Swift 2.0 or later

  下面一個函數(shù)的功能是計算兩個double(optional)類型的數(shù)的相除的結(jié)果。在執(zhí)行除法之前,必須提前滿足三個條件:

  被除數(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ù)可以正常使用,但是會存在兩個問題:

  那些前提條件可以利用guard語句。

  使用了強制拆包。

  請使用guard語句和避免使用強制拆包來優(yōu)化這個函數(shù)。

  答案:guard語句是在Swift 2.0中引進的,它是用途是在未滿足某個條件時,提供一個退出的路徑。對于檢查是否滿足先決條件來說,它是非常有用的。因為它可以使你更清晰的表達邏輯——而不是像i各種f語句嵌套實現(xiàn)那么復雜。下面就是一個例子:

  guard let dividend = dividend else { return .None }

  它也可以在optional binding(可選綁定)中使用。使用guard語句之后,使拆包后的變量可以被訪問。

  所以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)隱身的可拆包的運算在代碼的最后一行,因為dividend和divisor這兩個參數(shù)已經(jīng)被拆包并且以分別以一個常量來存儲。

  因此,你可以再次使用guard語句,使上面的函數(shù)更簡潔:

  func divide(dividend: Double?, by divisor: Double?) -> Double? {

  guard let dividend = dividend, divisor = divisor where divisor != 0 else { return .None }

  return dividend / divisor

  }

  上面的函數(shù)中使用了兩個guard語句,因為使用了where語句指定了divisor不能為0的條件。

  高級

  問題1- Swift 1.0 or later

  下面是thermometer作為結(jié)構(gòu)體的例子:

  public struct Thermometer {

  public var temperature: Double

  public init(temperature: Double) {

  self.temperature = temperature

  }

  }

  創(chuàng)建一個thermometer實例對象,你可以使用下面的代碼:

  var t: Thermometer = Thermometer(temperature:56.8)

  但是像下面的代碼那樣初始化對象是否會更好:

  var thermometer: Thermometer = 56.8

  能這樣做嗎?如果可以,怎么做?提示:it has to do with convertibles, but not convertibles like Camaros and Mustangs

  答案:Swift 定義了下面的協(xié)議,這些協(xié)議可以使一種類型通過字面量的方式來初始化并賦值。

  NilLiteralConvertible

  BooleanLiteralConvertible

  IntegerLiteralConvertible

  FloatLiteralConvertible

  UnicodeScalarLiteralConvertible

  ExtendedGraphemeClusterLiteralConvertible

  StringLiteralConvertible

  ArrayLiteralConvertible

  DictionaryLiteralConvertible

  采用相應的協(xié)議并且提供一個允許字面量初始化的公用方法。在Thermometer類型的例子下,我們需要實現(xiàn)FloatLiteralConvertible協(xié)議,代碼如下:

  extension Thermometer : FloatLiteralConvertible {

  public init(floatLiteral value: FloatLiteralType) {

  self.init(temperature: value)

  }

  }

  那么現(xiàn)在,你就可以通過一個簡單的float數(shù)字創(chuàng)建一Thermometer對象,代碼如下:

  var thermometer: Thermometer = 56.8

  問題2 – Swift 1.0 or later

  Swift 擁有一系列預定義的運算符,這些運算符執(zhí)行不同類型的操作,例如算術(shù)運算符和邏輯運算符。它甚至允許創(chuàng)建自定義的運算符,無論是一元運算符還是二元運算符。自定義一個滿足一下規(guī)格的冪運算符:

  以兩個整數(shù)作為參數(shù)

  返回第一個參數(shù)的第二個參數(shù)次方的值

  忽略潛在溢出錯誤

  答案:創(chuàng)建一個自定義的運算符需要兩個步驟:聲明它和實現(xiàn)它。

  使用operator關(guān)鍵字來聲明指定的類型(一元或者二元)、組成這個運算符字符的順序已經(jīng)它的優(yōu)先級和關(guān)聯(lián)性。

  在這中情況下,運算符是^^,類型是infix(二進制),關(guān)聯(lián)性是right,優(yōu)先級設置成為155,原因是乘法和除法的優(yōu)先級是150.下面就是具體的聲明代碼:

  infix operator ^^ { associativity right precedence 155 }

  代碼實現(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,就會發(fā)生運行時錯誤。

  問題3 – Swift 1.0 or later

  你能像下面的代碼一樣使用原始值定義一個枚舉類型嗎?如果不行,說明原因。

  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)換成下列類型中的任何一個類型:

  a.Int

  b.String

  c. Character

  在上面的代碼中,原始值即使是獨立的個體值,但是它仍然是一個不兼容的元組。

  問題4- Swift 2.0 or later

  下面的代碼定義了一個結(jié)構(gòu)體Pizza和一個協(xié)議Pizzeria,這個協(xié)議有一個包含makeMargherita()函數(shù)的擴展。

  struct Pizza {

  let ingredients: [String]

  }

  protocol Pizzeria {

  func makePizza(ingredients: [String]) -> Pizza

  func makeMargherita() -> Pizza

  }

  extension Pizzeria {

  func makeMargherita() -> Pizza {

  return makePizza(["tomato", "mozzarella"])

  }

  }

  現(xiàn)在你將要定義一個如下的Lombardi的餐館:

  struct Lombardis: Pizzeria {

  func makePizza(ingredients: [String]) -> Pizza {

  return Pizza(ingredients: ingredients)

  }

  func makeMargherita() -> Pizza {

  return makePizza(["tomato", "basil", "mozzarella"])

  }

  }

  下面的代碼創(chuàng)建了Lombardis類型的兩個實例對象,哪一個對象會產(chǎn)生一個帶有basil的margherita披薩?

  let lombardis1: Pizzeria = Lombardis()

  let lombardis2: Lombardis = Lombardis()

  lombardis1.makeMargherita()

  lombardis2.makeMargherita()

  答案:兩個都可以。Pizzeria協(xié)議聲明了makeMargherita()方法并且提供了一個默認的實現(xiàn),而且它又在Lombardis的實現(xiàn)中被重寫。在這兩種情況下,由于這個方法在協(xié)議中被聲明,那么在運行時相應的實現(xiàn)就會被調(diào)用。

  假如Pizzeria協(xié)議沒有聲明makeMargherita()方法,但是擴展中仍然提供了如下的代碼的這個方法默認的實現(xiàn),會發(fā)生什么?

  protocol Pizzeria {

  func makePizza(ingredients: [String]) -> Pizza

  }

  extension Pizzeria {

  func makeMargherita() -> Pizza {

  return makePizza(["tomato", "mozzarella"])

  }

  }

  這種情況下,只有l(wèi)ombardis2會產(chǎn)生一個帶有basil的pizza,而lombardis1將會產(chǎn)生一個不帶basil的pizza,原因是它會調(diào)用擴展中定義的那個方法。

  問題5- Swift 2.0 or later

  下面的代碼有一個編譯錯誤,請指出來并說明原因。

  struct Kitten {

  }

  func showKitten(kitten: Kitten?) {

  guard let k = kitten else {

  print("There is no kitten")

  }

  print(k)

  }

  提示:有三種方法修復它。

  答案:guard語句中得else語句必須需要一個返回路徑,你可以使用return ,拋出異;蛘哒{(diào)用@noreturn.最簡單的解決方法是添加一個return語句,代碼如下:

  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)用一個@noreturn功能函數(shù) fatalError( ) 解決方案:

  struct Kitten {

  }

  func showKitten(kitten: Kitten?) {

  guard let k = kitten else {

  print("There is no kitten")

  fatalError()

  }

  print(k)

  }

  面試試題

  你很棒,但是你還沒達到絕地武士的要求。任何人都可以寫出代碼,但是你如何處理理論和實踐相結(jié)合的開放式問題?

  為了解決這些問題,你仍然需要使用Playgroud寫代碼來驗證一些問題。

  初級

  問題1- Swift 1.0 or later

  什么是optional類型,它是用來解決什么問題的?

  答案:optional類型被用來表示任何類型的變量都可以表示缺少值。在Objective-C中,引用類型的變量是可以缺少值得,并且使用nil作為缺少值;镜臄(shù)據(jù)類型如int 或者float沒有這種功能。

  Swift用optional擴展了在基本數(shù)據(jù)類型和引用類型中缺少值的概念。一個optional類型的變量,在任何時候都可以保存一個值或者為nil。

  問題2- Swift 1.0 or later

  在Swfit中,什么時候用結(jié)構(gòu)體,什么時候用類?

  答案:一直都有這樣的爭論:到底是用類的做法優(yōu)于用結(jié)構(gòu)體,還是用結(jié)構(gòu)體的做法優(yōu)于類。函數(shù)式編程傾向于值類型,面向?qū)ο缶幊谈矚g類。

  在Swift 中,類和結(jié)構(gòu)體有許多不同的特性。下面是兩者不同的總結(jié):

  類支持繼承,結(jié)構(gòu)體不支持。

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

  并沒有通用的規(guī)則決定結(jié)構(gòu)體和類哪一個更好用。一般的建議是使用最小的工具來完成你的目標,但是有一個好的經(jīng)驗是多使用結(jié)構(gòu)體,除非你用了繼承和引用語義。

  想要了解更多,點擊這里。

  注意:在運行時,結(jié)構(gòu)體的在性能方面更優(yōu)于類,原因是結(jié)構(gòu)體的方法調(diào)用是靜態(tài)綁定,而類的方法調(diào)用是動態(tài)實現(xiàn)的。這就是盡可能得使用結(jié)構(gòu)體代替類的又一個好的原因。

  問題3- Swift 1.0 or later

  什么是泛型?泛型是用來解決什么問題的?

  答案:泛型是用來使類型和算法安全的工作的一種類型。在Swift中,在函數(shù)和數(shù)據(jù)結(jié)構(gòu)中都可以使用泛型,例如類、結(jié)構(gòu)體和枚舉。

  泛型一般是用來解決代碼復用的問題。常見的一種情況是,你有一個函數(shù),它帶有一個參數(shù),參數(shù)類型是A,然而當參數(shù)類型改變成B的時候,你不得不復制這個函數(shù)。

  例如,下面的代碼中第二個函數(shù)就是復制第一個函數(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) // 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

  這個代碼會按照預期的方式工作,但是它在編譯時不安全。它允許字符串和整數(shù)相比較,像這樣:

  areTheyEqual(1, "ray")

  應用程序不會崩潰,但是允許字符串和整數(shù)相比較可能不是預想的結(jié)果。

  通過采用泛型,可以合并這兩個函數(shù)為一個并同時保持類型安全。下面是代碼實現(xiàn):

  func areTheyEqual(x: T, _ y: T) -> Bool {

  return x == y

  }

  areTheyEqual("ray", "ray")

  areTheyEqual(1, 1)

  上面的例子是測試兩個參數(shù)是否相等,這兩個參數(shù)的類型受到約束都必須遵循Equatable協(xié)議。上面的代碼達到預想的結(jié)果,并且防止了傳遞不同類型的參數(shù)。

  問題4- Swift 1.0 or later

  哪些情況下你不得不使用隱式拆包?說明原因。

  答案:對optional變量使用隱式拆包最常見的原因如下:

  1、對象屬性在初始化的時候不能nil,否則不能被初始化。典型的例子是Interface Builder outlet類型的屬性,它總是在它的擁有者初始化之后再初始化。在這種特定的情況下,假設它在Interface Builder中被正確的配置——outlet被使用之前,保證它不為nil。

  2、解決強引用的循環(huán)問題——當兩個實例對象相互引用,并且對引用的實例對象的值要求不能為nil時候。在這種情況下,引用的一方可以標記為unowned,另一方使用隱式拆包。

  建議:除非必要,不要對option類型使用隱式拆包。使用不當會增加運行時崩潰的可能性。在某些情況下,崩潰可能是有意的行為,但有更好的方法來達到相同的結(jié)果,例如,通過使用fatalError( )函數(shù)。

  問題5- Swift 1.0 or later

  對一個optional變量拆包有多少種方法?并在安全方面進行評價。

  答案:

  強制拆包 !操作符——不安全

  隱式拆包變量聲明——大多數(shù)情況下不安全

  可選綁定——安全

  自判斷鏈接(optional chaining)——安全

  nil coalescing 運算符(空值合并運算符)——安全

  Swift 2.0 的新特性 guard 語句——安全

  Swift 2.0 的新特性optional pattern(可選模式) ——安全(@Kametrixom支持)

  中級

  問題1- Swift 1.0 or later

  Swift 是面向?qū)ο缶幊陶Z言還是函數(shù)式編程語言?

  答案:Swift是一種混合編程語言,它包含這兩種編程模式。它實現(xiàn)了面向?qū)ο蟮娜齻基本原則:

  封裝

  繼承

  多態(tài)

  說道Swift作為一種函數(shù)式編程語言,我們就不得不說一下什么是函數(shù)式編程。有很多不同的方法去定義函數(shù)式編程語言,但是他們表達的意義相同。

  最常見的定義來自維基百科:…它是一種編程規(guī)范…它把電腦運算當做數(shù)學函數(shù)計算,避免狀態(tài)改變和數(shù)據(jù)改變。

  很難說Swift是一個成熟的函數(shù)式語言,但是它已經(jīng)具備了函數(shù)式語言的基礎(chǔ)。

  問題2- Swift 1.0 or later

  下面的功能特性都包含在Swift中嗎?

  1、泛型類

  2、泛型結(jié)構(gòu)體

  3、泛型協(xié)議

  答案:

  Swift 包含1和2特性。泛型可以在類、結(jié)構(gòu)體、枚舉、全局函數(shù)或者方法中使用。

  3是通過typealias部分實現(xiàn)的。typealias不是一個泛型類型,它只是一個占位符的名字。它通常是作為關(guān)聯(lián)類型被引用,只有協(xié)議被一個類型引用的時候它才被定義。

  問題3- Swift 1.0 or later

  在Objective-C中,一個常量可以這樣定義:

  const int number = 0;

  類似的Swift是這樣定義的:

  let number = 0

  兩者之間有什么不同嗎?如果有,請說明原因。

  答案:const常量是一個在編譯時或者編譯解析時被初始化的變量。通過let創(chuàng)建的是一個運行時常量,是不可變得。它可以使用stattic 或者dynamic關(guān)鍵字來初始化。謹記它的的值只能被分配一次。

  問題4- Swift 1.0 or later

  聲明一個靜態(tài)屬性或者函數(shù),我們常常使用值類型的static修飾符。下面就是一個結(jié)構(gòu)體的例子:

  struct Sun {

  static func illuminate() {}

  }

  對類來說,使用static 或者class修飾符,都是可以的。它們使用后的效果是一樣的,但是本質(zhì)上是不同的。能解釋一下為什么不同嗎?

  答案:

  static修飾的屬性或者修飾的函數(shù)都不可以重寫。但是使用class修飾符,你可以重寫屬性或者函數(shù)。

  當static在類中應用的時候,static就成為class final的一個別名。

  例如,在下面的代碼中,當你嘗試重寫illuminate()函數(shù)時,編譯器就會報錯:

  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()

  }

  }

  問題5- Swift 1.0 or later

  你能通過extension(擴展)保存一個屬性嗎?請解釋一下原因。

  答案:不能。擴展可以給當前的類型添加新的行為,但是不能改變本身的類型或者本身的接口。如果你添加一個新的可存儲的屬性,你需要額外的內(nèi)存來存儲新的值。擴展并不能實現(xiàn)這樣的任務。

  高級

  問題1- Swift 1.2

  在Swift1.2版本中,你能解釋一下用泛型來聲明枚舉的問題嗎?拿下面代碼中Either枚舉來舉例說明吧,它有兩個泛型類型的參數(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)

  }

  提示:驗證上面的條件,需要在Xcode工程里面,而不是在Playgroud中。同時注意,這個問題跟Swift1.2相關(guān),所以Xcode的版本必須是6.4以上。

  答案:上面的代碼會出現(xiàn)編譯錯誤:

  unimplemented IR generation feature non-fixed multi-payload enum layout

  問題是T的內(nèi)存大小不能確定前期,因為它依賴于T類型本身,但enum情況下需要一個固定大小的有效載荷。

  最常用的解決方法是講泛類型用引用類型包裝起來,通常稱為box,代碼如下:

  class Box{

  let value: T

  init(_ value: T) {

  self.value = value

  }

  }

  enum Either{

  case Left(Box)

  case Right(Box)

  }

  這個問題在Swift1.0及之后的版本出現(xiàn),但是Swift2.0的時候,被解決了。

  問題2- Swift 1.0 or later

  閉包是引用類型嗎?

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

  問題3- Swift 1.0 or later

  UInt類型是用來存儲無符號整型的。下面的代碼實現(xiàn)了一個有符號整型轉(zhuǎn)換的初始化方法:

  init(_ value: Int)

  然而,在下面的代碼中,當你給一個負值的時候,它會產(chǎn)生一個編譯時錯誤:

  let myNegative = UInt(-1)

  我們知道負數(shù)的內(nèi)部結(jié)構(gòu)是使用二進制補碼的正數(shù),在保持這個負數(shù)內(nèi)存地址不變的情況下,如何把一個負整數(shù)轉(zhuǎn)換成一個無符號的整數(shù)?

  答案:使用下面的初始化方法:

  UInt(bitPattern: Int)

  問題4- Swift 1.0 or later

  描述一種在Swift中出現(xiàn)循環(huán)引用的情況,并說明怎么解決。

  答案:循環(huán)引用出現(xiàn)在當兩個實例對象相互擁有強引用關(guān)系的時候,這會造成內(nèi)存泄露,原因是這兩個對像都不會被釋放。只要一個對象被另一個對象強引用,那么該對象就不能被釋放,由于強引用的存在,每個對象都會保持對方存在。

  解決這個問題的方法是,用weak或者unowned引用代替其中一個的強引用,來打破循環(huán)引用。

  問題5- Swift 2.0 or later

  Swift2.0 增加了一個新的關(guān)鍵字來實現(xiàn)遞歸枚舉。下面的例子是一個枚舉類型,它在Node條件下有兩個相關(guān)聯(lián)的值類型T和List:

  enum List{

  case Node(T, List)

  }

  什么關(guān)鍵字可以實現(xiàn)遞歸枚舉?

  答案:indirect 關(guān)鍵值可以允許遞歸枚舉,代碼如下:

  enum List{

  indirect case Cons(T, List)

  }

  Where To Go From Here?

  恭喜你到了文章的最后,如果你不知道所有問題的答案,也不要感到沮喪。

  因為上面中得有些問題還是比較復雜的,并且Swift是一門富有表現(xiàn)力的語言,還有很多需要我們學。此外,蘋果公司一直改善Swift的新特性,所以即使學的最好的人也不可能知道所有的一切。

  事實上,學習一門語言最好的方式是用它。在你的工程里或者Plaugroud里面使用Swift編程。Swift幾乎可以無縫銜接Object-C,所以在你現(xiàn)有的工程中使用Swift是一個學習Swift的很好的方法。

【Swift 面試題及其答案】相關(guān)文章:

面試題及答案02-06

面試的問題及其答案07-23

hr面試題及答案12-30

必備.net面試題及答案08-02

護士面試題目及答案02-22

小升初面試題目及答案09-25

Hadoop面試題目及答案07-26

文秘面試題目及答案08-09

Google公司經(jīng)典面試題及答案02-23

浙江國稅面試題及答案02-07