Swift 面試題

Swift語言至今誕生有一年多的時間了,已經(jīng)成為當(dāng)前最流行語言之一盈简。雖然它的語法簡單好用,但實際上Swift是一門非常復(fù)雜的語言太示。因為它不僅是面向?qū)ο蟮耐瑫r又是函數(shù)式編程語言柠贤。本文主要介紹Swift常見的一些面試問題,你可以用這些問題向面試者提問类缤,也可以用來測試你自己目前所掌握的Swift知識臼勉,如果你不清楚問題答案的話也不用太擔(dān)心,因為每個問題下面都有相應(yīng)的答案餐弱。

問題主要分為兩個部分宴霸,筆試題和口頭問題。

筆試題:可以通過發(fā)送Email方式進行編程測試膏蚓,問題可以包含一小段代碼測試瓢谢。
口頭問題:可以通過手機等方式進行面對面的交流,因為這些問題更適合用語言進行交流驮瞧。

而且每個部分又分為三個等級:

初級:適用于剛接觸Swift的學(xué)習(xí)者氓扛,已經(jīng)讀過一本或者兩本Swift相關(guān)書籍,開始準備在App中使用Swift語言论笔。
中級:適用于對Swift語言概念非常感興趣的學(xué)習(xí)者采郎,已經(jīng)讀過許多Swift博客文章并且想進一步深入學(xué)習(xí)Swift語言。
高級:適用于高級學(xué)者狂魔,已經(jīng)對Swift語言很熟悉蒜埋,喜歡探尋語言,想進一步挑戰(zhàn)自我最楷,喜歡高級技術(shù)者整份。

如果你想知道這些問題的答案,建議你最好打開Playground管嬉,親自實現(xiàn)編碼皂林。下面這些問題的答案都在Xcode 7.0 beta 6上測試過。

筆試題

初級

你好蚯撩,現(xiàn)在開始基礎(chǔ)測試础倍。
問題 #1 - Swift1.0或者更高版本
用下面方法寫一個for循環(huán)是最好的方法嗎?

for var i = 0; i < 5; i++ {
  print("Hello!")
}

答案:

for _ in 0...4 {
  print("Hello!")
}

Swift實現(xiàn)了兩種范圍操作符胎挎,分別為閉區(qū)間操作符和半開區(qū)間操作符沟启。前者包括范圍內(nèi)的所有元素忆家。例如下面例子包含了0到4所有元素。

0...4

而半開區(qū)間操作符不會包括最后一個元素德迹。下面的例子同樣包括了0到4所有元素

0..<5

問題 #2 - Swift1.0或者更高版本
考慮以下這段代碼

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)體是值類型而不是引用類型筷转,它是值copy。
下面這行代碼會首先創(chuàng)建一份tutorial1的copy悬而,然后再賦值給tutorial2

var tutorial2 = tutorial1

從這行代碼可以看出呜舒,tutorial2 的變化不會影響tutorial1
如果Tutorial是一個類,tutorial1.difficulty和tutorial2.difficulty的值都是2笨奠。
在Swift中袭蝗,類是引用類型,tutorial1的變化會影響到tutorial2般婆,反之亦然到腥。

問題 #3 - Swift1.0或者更高版本
用var聲明view1和用let聲明view2,在下面的例子中有什么不同腺兴?左电,最后一行Code能否編譯通過?

import UIKit
 
var view1 = UIView()
view1.alpha = 0.5
 
let view2 = UIView()
view2.alpha = 0.5 // Will this line compile?

答案

因為view1是一個變量页响,所以可以用UIView的實例進行賦值篓足,用let關(guān)鍵字只能賦值一次,所以下面代碼不能編譯成功闰蚕。

view2 = view1 // 錯誤:view2是不可變的

但是栈拖,UIView是基于類引用的,所以view2的屬性是可以改變的(最后一行代碼可以編譯通過)

let view2 = UIView()
view2.alpha = 0.5 // Yes!

問題 #4 - Swift1.0或者更高版本
下面Code是把數(shù)組按字母順序進行排序没陡,看起來有些復(fù)雜涩哟,你能用閉包簡化它嗎?

let animals = ["fish", "cat", "chicken", "dog"]
let sortedAnimals = animals.sort { (one: String, two: String) -> Bool in
  return one < two
}

答案

首先是可以簡化閉包的參數(shù)盼玄,因為在閉包中贴彼,系統(tǒng)是可以通過類型推斷方式推算出參數(shù)的類型。所以你可以去掉參數(shù)的類型:

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

返回類型也可以推算出來埃儿,所以可以去掉閉包的返回類型:

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

可以用$i符號替換掉參數(shù)的名字器仗,代碼然后就變成這樣:

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

在單語句的閉包中,關(guān)鍵字return也可以省略。最后一條語句的返回值就變成閉包的返回值:

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

Oops, 到目前精钮,是不是非常簡單了威鹿,但實際上并非如此。
對字符串來說轨香,有一個字符串比較函數(shù)忽你,定義如下:

func <(lhs: String, rhs: String) -> Bool

使用這個函數(shù)可以讓你的Code更加簡潔, 如下:

let sortedAnimals = animals.sort(<)

注意:以上每一步的代碼編譯運行后都會輸出同樣的結(jié)果,你可以選擇使用單字節(jié)的閉包臂容。

問題 #5 - Swift1.0或者更高版本
下面代碼定義AddressPerson兩個類科雳,創(chuàng)建Ray和Brian兩個實例。

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)

假如Brian要搬遷到新的地址居住脓杉,所以你會這樣更新他的住址:

brian.address.fullAddress = "148 Tutorial Street"

這樣做的話會發(fā)生什么炸渡?錯在哪個地方?

I know it

答案

Ray也會更新地址丽已。因為類Address是引用類型,headquarters是同一個實例买决,不論你是修改ray的地址還是brian的地址沛婴,都會改變headquarters地址。
解決方法是為brian新創(chuàng)建一個地址督赤∴业疲或者聲明Address為結(jié)構(gòu)體而不是類。

中級

下面提升一下難度的等級
問題 #1 - Swift2.0或者更高版本
思考一下以下代碼:

var optional1: String? = nil
var optional2: String? = .None

nil.None有什么不同躲舌?變量optional1和optional2有什么不同丑婿?
答案:

nil.None是一樣的。當(dāng)可選變量沒有值時没卸,Optional.None(.None for short)是一般初始化可選變量的方式羹奉,而nil則是另一種寫法。
事實上约计,下面條件語句輸出是true:

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

記著下面代碼說明enumeration是一個可選類型:

enum Optional<T> {
  case None
  case Some(T)
}

問題 #2 - Swift1.0或者更高版本
下面是用類和結(jié)構(gòu)體實現(xiàn)溫度計的例子:

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)

這段代碼在哪個地方會出現(xiàn)編譯錯誤诀拭?為什么?
小提示:在Playground測試之前煤蚌,請仔細閱讀并思考代碼耕挨。
答案:

在最后一行編譯器會提示錯誤,結(jié)構(gòu)體ThermometerStruct聲明一個可變的函數(shù)修改內(nèi)部變量 temperature尉桩,但是registerTemperature卻被一個用let創(chuàng)建的實例所調(diào)用筒占,用let定義的變量是不可變的,所以編譯通不過蜘犁。
在結(jié)構(gòu)體中翰苫,改變內(nèi)部狀態(tài)的方法必須用mutating聲明,而且不允許用不可變的實例調(diào)用它沽瘦。

問題 #3 - Swift1.0或者更高版本
下面的代碼打印輸出是什么革骨?為什么农尖?

var thing = "cars"
let closure = { [thing] in
  print("I love \(thing)")
}
thing = "airplanes"
closure()

答案

結(jié)果會打印出“I love cars”。當(dāng)聲明閉包的時候良哲,捕獲列表會創(chuàng)建一份thing的copy盛卡,所以被捕獲到的值是不會改變的,即使你改變thing的值筑凫。
如果你去掉閉包中的捕獲列表滑沧,編譯器會使用引用代替copy。在這種情況下巍实,當(dāng)閉包被調(diào)用時滓技,變量的值是可以改變的。示例如下:

var thing = "cars"
let closure = {    
  print("I love \(thing)")
}
thing = "airplanes"
closure() // Prints "I love airplanes"

問題 #4 - Swift2.0或者更高版本
這是一個全局函數(shù)棚潦,用來記錄數(shù)組中唯一值的數(shù)量令漂。

func countUniques<T: Comparable>(array: Array<T>) -> 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類型需要遵循Comparable協(xié)議丸边。
可以像下面這樣調(diào)用它:

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

重寫這個函數(shù)叠必,讓它作為數(shù)組Array的方法,使得可以這樣調(diào)用:

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

答案

在Swift2.0中妹窖,泛型是可以被擴展的纬朝,但需要類型約束限制,如果泛型不滿足約束骄呼,那么擴展也是不可見或者不可訪問的共苛。
因此,全局函數(shù)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
  }
}

注意這個被重寫的方法只有當(dāng)泛型的類型Element實現(xiàn)了Comparable協(xié)議才是有效的蜓萄。例如隅茎,當(dāng)你用裝滿UIView的數(shù)組調(diào)用這個方法時編譯器會提示錯誤:

import UIKit
let a = [UIView(), UIView()]
a.countUniques() // compiler error here because UIView doesn't implement Comparable

問題 #5 - Swift2.0或者更高版本
下面是一個計算兩個可選類型除法的函數(shù)。在計算之前滿足以下三個條件:

  • 被除數(shù)不能為空(not nil)
  • 除數(shù)不能為空(not nil)
  • 除數(shù)不能為0
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!
}

這段代碼能夠按照預(yù)期的那樣進行工作绕德,但同時存在兩個問題:

  • 前置條件患膛,可以考慮利用guard關(guān)鍵字
  • 使用了強制解包
    改進這個函數(shù),使用guard條件并且避免使用強制解包.
    答案

在Swift2.0中引用guard語句關(guān)鍵字耻蛇,如果不滿足時踪蹬,guard會提供一個退出路徑。這個關(guān)鍵字在判斷預(yù)置條件時非常有用臣咖,能更加清晰表達條件跃捣,不需要采用金字塔似的多重嵌套if語句,下面是一個例子:

guard dividend != .None else { return .None }

它還可以用在可選綁定上夺蛇,能夠訪問解包后的變量:

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
}

注意在最后一行沒有隱士解包操作符疚漆,是因為dividend和divisor已經(jīng)被解包存儲,并且是非可選不可變的。
而且可以成組地監(jiān)視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
}

現(xiàn)在只有兩個guard條件闻镶,因為使用where語句判斷解包后的變量divisor是否為0。

圖片來自簡書

高級

問題 #1 - Swift1.0或者更高版本
思考以下用結(jié)構(gòu)體定義的溫度計:

public struct Thermometer {
  public var temperature: Double
  public init(temperature: Double) {
    self.temperature = temperature
  }
}

創(chuàng)建一個實例丸升,可以用下面的方式:

var t: Thermometer = Thermometer(temperature:56.8)

但最好是使用下面方式進行初始化:

var thermometer: Thermometer = 56.8

這可以做到嗎铆农?應(yīng)該怎樣做?
答案

Swift定義了以下協(xié)議狡耻,通過使用賦值操作符墩剖,用字面值直接初始化:

  • NilLiteralConvertible
  • BooleanLiteralConvertible
  • IntegerLiteralConvertible
  • FloatLiteralConvertible
  • UnicodeScalarLiteralConvertible
  • ExtendedGraphemeClusterLiteralConvertible
  • StringLiteralConvertible
  • ArrayLiteralConvertible
  • DictionaryLiteralConvertible
    采用相對應(yīng)的協(xié)議,提供一個公有的構(gòu)造器夷狰,允許字面值方式初始化它岭皂。在這個溫度計這個例子中,需要實現(xiàn)FloatLiteralConvertible協(xié)議:
extension Thermometer : FloatLiteralConvertible {
  public init(floatLiteral value: FloatLiteralType) {
    self.init(temperature: value)
  }
}

現(xiàn)在可以使用float值創(chuàng)建一個實例了:

var thermometer: Thermometer = 56.8

問題 #2 - Swift1.0或者更高版本
Swift中定義了運算操作符和邏輯操作符沼头,用于操作不同的類型爷绘。當(dāng)然,你可以自定義一些運算操作符进倍,比如一元或者二元的揉阎。
定義一個^^冪操作符,并且同時滿足以下要求:

  • 用兩個Int型整數(shù)作為參數(shù)背捌。
  • 返回冪函數(shù)的值,第一個參數(shù)為底數(shù)洞斯,第二個參數(shù)為指數(shù)毡庆。
  • 實現(xiàn)潛在的溢出錯誤。

答案

創(chuàng)建一個自定義運算符需要兩步:聲明和實現(xiàn)烙如。
聲明使用關(guān)鍵字operator么抗,用于指定類型(一元或者二元),然后指定運算符的結(jié)合性和優(yōu)先級亚铁。
在本例中蝇刀,運算符是^^,類型是infix徘溢,結(jié)合性是右結(jié)合吞琐,優(yōu)先級設(shè)置為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)
}

注意一點站粟,實現(xiàn)沒有考慮溢出的情況,如果運算結(jié)果超過Int.max,將會產(chǎn)生運行時錯誤曾雕。

問題 #3 - Swift1.0或者更高版本
你能像下面這樣用原始值定義枚舉類型嗎奴烙?為什么?

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

答案

不能,原始值的類型必須遵循以下條件:

  • 實現(xiàn)Equatable協(xié)議
  • 必須是以下文字轉(zhuǎn)換的類型:
    • Int
    • String
    • Character

在以上代碼中切诀,原始數(shù)值類型是元組類型揩环,即使元組中的數(shù)值滿足條件,也是不兼容的幅虑。

問題 #4 - Swift2.0或者更高版本
思考以下代碼丰滑,定義了結(jié)構(gòu)體Pizza,協(xié)議Pizzeria翘单,在協(xié)議擴展中實現(xiàn)默認方法makeMargherita()吨枉。

struct Pizza {
  let ingredients: [String]
}
 
protocol Pizzeria {
  func makePizza(ingredients: [String]) -> Pizza
  func makeMargherita() -> Pizza
}
 
extension Pizzeria {
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "mozzarella"])
  }
}

然后定義一個餐館Lombardis:

struct Lombardis: Pizzeria {
  func makePizza(ingredients: [String]) -> Pizza {
    return Pizza(ingredients: ingredients)
  }
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "basil", "mozzarella"])
  }
}

下面代碼創(chuàng)建了Lombardis的兩個實例。哪一個會使用“basil”來做披薩哄芜?

let lombardis1: Pizzeria = Lombardis()
let lombardis2: Lombardis = Lombardis()
 
lombardis1.makeMargherita()
lombardis2.makeMargherita()

答案

兩個都會貌亭。協(xié)議Pizzeria聲明了makeMargherita()方法,并且提供了一個默認的實現(xiàn)认臊。在Lombardis實現(xiàn)中圃庭,這個方法被重寫了。因為這個方法在協(xié)議中聲明了失晴,在Runtime時能正確的被調(diào)用剧腻。
如果協(xié)議中沒有聲明makeMargherita()方法,但是在協(xié)議的擴展中又默認實現(xiàn)了 這個方法會怎樣涂屁?

protocol Pizzeria {
  func makePizza(ingredients: [String]) -> Pizza
}
extension Pizzeria {
  func makeMargherita() -> Pizza {
    return makePizza(["tomato", "mozzarella"])
  }
}

在這種情況下书在,只有l(wèi)ombardis2能用“basil”來做披薩,而lombardis1則不會拆又,因為它調(diào)用的是擴展中默認的方法儒旬。

問題 #5 - Swift2.0或者更高版本
下面代碼會有編譯錯誤,你能指出在哪個地方嗎帖族?為什么栈源?

struct Kitten {
}
 
func showKitten(kitten: Kitten?) {
  guard let k = kitten else {
    print("There is no kitten")
  }
 
  print(k)
}

提示:有三種方式可以fix它
答案

在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)
}

口頭答疑

絕地戰(zhàn)士

初級

問題 #1 - Swift1.0或者更高版本
可選是什么涣雕?可以解決什么問題艰亮?
答案

可選可以使得任何類型的變量都能表達缺省值。在Objective-C中挣郭,缺省值只適用于引用類型垃杖,通常被指定為nil。對于基礎(chǔ)類型的變量(例如int, float)則沒有這個功能丈屹,
而Swift把缺省值概念擴展到引用類型和值類型中调俘。一個可選變量在任何時候可以有值或者為nil伶棒。

問題 #2 - Swift1.0或者更高版本
什么時候使用結(jié)構(gòu)體?什么時候使用類彩库?
答案

目前關(guān)于使用類還是結(jié)構(gòu)體這個問題肤无,有許多的爭論。在函數(shù)式編程傾向于使用值類型骇钦,而面向?qū)ο缶幊讨懈矚g用類宛渐。
在Swift中,類和結(jié)構(gòu)體有很多不相同的功能眯搭,主要有下面幾點:

  • 類支持繼承窥翩,而結(jié)構(gòu)體不支持。
  • 類是引用類型鳞仙,結(jié)構(gòu)體是值類型

并沒有統(tǒng)一的規(guī)則決定孰好孰壞寇蚊。通常推薦使用最適合的工具完成特定的目標,在swift中比較好的做法是使用結(jié)構(gòu)體棍好,除非你需要用到繼承或者引用的時候才使用類仗岸。
主要因為在運行時,結(jié)構(gòu)體的性能優(yōu)于類的,結(jié)構(gòu)體的方法調(diào)用是靜態(tài)綁定的,而類是在Runtime時動態(tài)解析的斯碌。

問題 #3 - Swift1.0或者更高版本
泛型是什么?用來解決什么問題扫尖?
答案

泛型可以使某一個類型的算法能夠更安全的工作。在Swift中泛型可以用在函數(shù)和數(shù)據(jù)類型上,如類,結(jié)構(gòu)體和枚舉類型积糯。
泛型還能解決代碼重復(fù)的問題。普遍現(xiàn)象是當(dāng)你已經(jīng)有一個帶參數(shù)的方法谦纱,但你又不得不再重新寫一遍有著類似類型的方法。
在下面的例子中君编,第二個函數(shù)就像是第一個函數(shù)的“clone”跨嘉,它只是把傳入?yún)?shù)的類型從字符串變?yōu)檎汀?/p>

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

雖然這種方式是能解決問題吃嘿,但是在編譯期間是不安全的祠乃。因為它會允許比較String類型和Integer類型,就像下面這樣:

areTheyEqual(1, "ray")

雖然應(yīng)用程序不會crash兑燥,但是允許字符串和整型進行比較亮瓷,會出現(xiàn)讓你想不到的結(jié)果。
使用泛型的話降瞳,可以把兩個函數(shù)合成一個嘱支,同時又能保證類型是安全的蚓胸。下面是一個具體實現(xiàn):

func areTheyEqual<T: Equatable>(x: T, _ y: T) -> Bool {
  return x == y
}
areTheyEqual("ray", "ray")
areTheyEqual(1, 1)

這個例子是測試兩個數(shù)是否相等,你可以限制傳入?yún)?shù)類型是任意類型除师,只要這個類型實現(xiàn)了Equatable協(xié)議沛膳。這段代碼可以得到你想要的結(jié)果并且能防止傳入不同參數(shù)類型。

Two birds, one stone

問題 #4 - Swift1.0或者更高版本
在某些情況下汛聚,你不得不使用隱式解包可選锹安?什么場合?為什么倚舀?
答案

下面情況下需要使用隱式解包可選:

  1. 當(dāng)屬性在初始化階段不能為空的時候叹哭。一個典型的例子是IB的輸出口,它總是要初始化的痕貌。使用它之前已經(jīng)在IB中配置风罩,outlet確保在使用之前值不為空。
  2. 解決強引用循環(huán)的問題芯侥,當(dāng)兩個實例相互引用泊交,需要一個實例是非空引用。在這種情況下柱查,一個實例可以標注unowned廓俭,另一個使用隱式解包可選。
    提示:不要使用隱式解包可選唉工,除非你必須使用時研乒。如果使用它不當(dāng)時,會增加Runtime時crash的機率淋硝。

問題 #5 - Swift1.0或者更高版本
解包可選類型方法是什么雹熬?它們的安全性怎樣?
提示:有7種方法
答案

  • forced unwrapping ! operator -- unsafe
  • implicitly unwrapped variable declaration -- unsafe in many cases
  • optional binding -- safe
  • optional chaining -- safe
  • nil coalescing operator -- safe
  • new Swift 2.0 guard statement -- safe
  • new Swift 2.0 optional pattern -- safe
Believe

中級

問題 #1 - Swift1.0或者更高版本
Swift是面向?qū)ο笳Z言還是函數(shù)式語言谣膳?
答案

Swift是混合式語言竿报,兩種都支持。
它實現(xiàn)面向?qū)ο蟮娜齻€基本特征:

  • 封裝
  • 繼承
  • 多態(tài)

和函數(shù)式語言相比继谚,Swift有一些不同烈菌,雖然它滿足函數(shù)式語言基本要求,但并不是完全成熟的函數(shù)式編程語言花履。

問題 #2 - Swift1.0或者更高版本
下面功能芽世,哪一個在Swift中有?

  1. 泛型類
  2. 泛型數(shù)據(jù)結(jié)構(gòu)
  3. 泛型協(xié)議

答案

1和2在Swift中有诡壁。泛型可以用在類济瓢,結(jié)構(gòu)體,枚舉類型妹卿,全局函數(shù)和方法中旺矾。
3可以通過關(guān)鍵字typealias部分實現(xiàn)蔑鹦。它并不是泛型,只是占位符名字而已宠漩。它常被看作是關(guān)聯(lián)數(shù)據(jù)類型举反,采用協(xié)議時才會被定義。

問題 #3 - Swift1.0或者更高版本
在Objective-C中扒吁,一個常量可以像下面進行聲明:

const int number = 0;

下面是Swift中的:

let number = 0

他們之間有什么不同嗎火鼻?如果是的,你能解釋一下嗎雕崩?
答案

const是一個變量在編譯期間被初始化值或者在編譯期間表達式的值魁索。
通過let關(guān)鍵字創(chuàng)建常量是在Runtime時初始化的,它能夠用用靜態(tài)的或者動態(tài)表達式的結(jié)果初始化盼铁。注意它的值只能被初始化一次粗蔚。

問題 #4 - Swift1.0或者更高版本
聲明一個靜態(tài)屬性或者函數(shù),你可以使用關(guān)鍵字static來修飾值類型饶火。以下是一個結(jié)構(gòu)體的例子:

struct Sun {
  static func illuminate() {}
}

對于類來說鹏控,你可以使用static或者class修飾符。雖然它們完成同樣的功能肤寝,但實際上是不同的当辐。你能解釋一下它們之間有什么不同嗎?

答案:

使用static關(guān)鍵字鲤看,靜態(tài)屬性和靜態(tài)函數(shù)是不能被重寫的缘揪,但當(dāng)使用class關(guān)鍵字,你可以重寫屬性和函數(shù)义桂。
其實找筝,對于類來說,static關(guān)鍵字是class final的別名而已慷吊。
例如袖裕,你編譯下面這些code時,當(dāng)你要重寫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 - Swift1.0或者更高版本
使用extension可以增加存儲屬性嗎急鳄?解釋一下
答案

是不能的。extension是用來給存在的類型添加新行為的嚷闭,并不能改變類型或者接口本身。如果你增加存儲屬性赖临,你需要額外的內(nèi)存空間存儲新的值胞锰。extension是不能管理這樣的任務(wù)的。

高級

問題 #1 - Swift1.2
在Swift1.2中兢榨,你能解釋一下用泛型聲明枚舉類型的問題嗎嗅榕?以兩個泛型T和V的枚舉類型Either為例顺饮,Left為關(guān)聯(lián)T類型,Right關(guān)聯(lián)V類型凌那。

enum Either<T, V> {
  case Left(T)
  case Right(V)
}

提示:檢驗這個問題要用Xcode工程兼雄,不要在Playground中。注意這個問題和Swift1.2有關(guān)帽蝶,你需要使用Xcode6.4版本赦肋。

答案

編譯的時候會提示以下問題:

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

問題在于不能提前知道T分配內(nèi)存大小,主要取決于T類型本身励稳。但是在枚舉類型中需要知道固定大小佃乘。
最常用的解決方法是采用一個泛型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>)
}

這個問題只會影響Swift1.0或之后版本驹尼,在Swift2.0中已經(jīng)解決趣避。

問題 #2 - Swift1.0或者更高版本
閉包是值類型還是引用類型?
答案

閉包是引用類型新翎。如果一個閉包賦值給一個變量程帕,這個變量又復(fù)制一份copy給另一個變量,那么變量所引用的閉包和捕獲的列表也會copy一份地啰。

問題 #3 - Swift1.0或者更高版本
UInt類型用來存儲無符號整數(shù)愁拭。它實現(xiàn)如下一個用有符號的整數(shù)構(gòu)造器:

init(_ value: Int)

但是如果你提供一個負整數(shù)的話,下面代碼會產(chǎn)生編譯錯誤髓绽。

let myNegative = UInt(-1)

知道計算機負數(shù)是用二進制補碼作為一個正數(shù)進行表示敛苇,你怎樣把Int類型的負整數(shù)轉(zhuǎn)為UInt數(shù)?

答案

已經(jīng)有一個初始化器可以完成:

UInt(bitPattern: Int)

問題 #4 - Swift1.0或者更高版本
你能描述一下在Swift中哪些地方會產(chǎn)生循環(huán)引用顺呕?有什么解決辦法嗎枫攀?

當(dāng)兩個實例之間相互進行強引用的時候,就會引起循環(huán)引用株茶。兩個實例都不會釋放內(nèi)存来涨,就會造成內(nèi)存泄露∑羰ⅲ可用weak或者unowned打破實例之間的強引用問題蹦掐,這樣兩個實例才有機會釋放內(nèi)存空間。

問題 #5 - Swift1.0或者更高版本
Swift2.0引用了一個新關(guān)鍵字能產(chǎn)生遞歸枚舉類型僵闯。下面是一個帶有Node節(jié)點的枚舉類型卧抗,Node關(guān)聯(lián)值類型,T和list:

enum List<T> {
    case Node(T, List<T>)
}

那個關(guān)鍵字是什么鳖粟?
答案

關(guān)鍵字indirect允許遞歸枚舉類型社裆,像下面這樣:

enum List<T> {
    indirect case Cons(T, List<T>)
}

結(jié)束語

所有的Swfit資源都來自于官方文檔《The Swift Programming Language》,學(xué)習(xí)一門語言最好的方法是使用它向图。所以可以在Playground使用它或者在實際項目中運用它泳秀,Swift能夠和Objective-C進行無縫對接标沪。本文是翻譯[Ray](1. http://www.raywenderlich.com/110982/swift-interview-questions-answers)的博客。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嗜傅,一起剝皮案震驚了整個濱河市金句,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吕嘀,老刑警劉巖违寞,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異币他,居然都是意外死亡坞靶,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門蝴悉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來彰阴,“玉大人,你說我怎么就攤上這事拍冠∧蛘猓” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵庆杜,是天一觀的道長射众。 經(jīng)常有香客問我,道長晃财,這世上最難降的妖魔是什么叨橱? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮断盛,結(jié)果婚禮上罗洗,老公的妹妹穿的比我還像新娘。我一直安慰自己钢猛,他們只是感情好伙菜,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著命迈,像睡著了一般贩绕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上壶愤,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天淑倾,我揣著相機與錄音,去河邊找鬼征椒。 笑死娇哆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播迂尝,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼剪芥!你這毒婦竟也來了垄开?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤税肪,失蹤者是張志新(化名)和其女友劉穎溉躲,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體益兄,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡锻梳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了净捅。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片疑枯。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蛔六,靈堂內(nèi)的尸體忽然破棺而出荆永,到底是詐尸還是另有隱情,我是刑警寧澤国章,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布具钥,位于F島的核電站,受9級特大地震影響液兽,放射性物質(zhì)發(fā)生泄漏骂删。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一四啰、第九天 我趴在偏房一處隱蔽的房頂上張望宁玫。 院中可真熱鬧,春花似錦拟逮、人聲如沸撬统。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恋追。三九已至,卻和暖如春罚屋,著一層夾襖步出監(jiān)牢的瞬間苦囱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工脾猛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留撕彤,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像羹铅,于是被迫代替她去往敵國和親蚀狰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容