Swift面試題

問題一:

下面代碼中變量 tutorial1.difficulty 和 tutorial2.difficulty 的值分別是什么? 如果 Tutorial 是一個類,會有什么不同嗎?為什么?

struct Tutorial {
  var difficulty: Int = 1
}

var tutorial1 = Tutorial()
var tutorial2 = tutorial1
tutorial2.difficulty = 2

回答:

tutorial1.difficulty 等于 1, tutorial2.difficulty 等于 2.
swift中的結(jié)構(gòu)體是值類型。是按值類型而不是引用類型復(fù)制值的。

創(chuàng)建了一個tutorial1的副本,并將其分配給tutorial2:

var tutorial2 = tutorial1
“ tutorial1 ”中未反映對“ tutorial12”的更改

如果 Tutorial 是一個類,那么 tutorial1 和 tutorial2 都等于 2.
swift中的類是引用類型。當(dāng)你更改Tutorial1的屬性時,你將看到它反映在Tutorial2中,反之亦然。

問題二:

你用var聲明了view1,用let聲明了view2。有什么區(qū)別,最后一行會編譯通過嗎?

import UIKit

var view1 = UIView()
view1.alpha = 0.5

let view2 = UIView()
view2.alpha = 0.5 // 此行是否編譯?

回答:

是的,最后一行可以編譯。view1 是一個變量,可以給它重新分配一個 UIView 類型的新實(shí)例。使用let,只能分配一次值,因此不會編譯以下代碼:

view2 = view1 // Error: view2 is immutable

UIView是一個具有引用語義的類,因此你可以改變view2的屬性-這意味著最后一行將編譯:

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

問題三:

這段復(fù)雜的代碼按字母順序?qū)γQ數(shù)組進(jìn)行排序。盡可能簡化閉包。

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

回答:

類型推斷系統(tǒng)會自動判斷閉包中參數(shù)的類型和返回類型,就可以去掉類型:

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

可以用$I符號替換參數(shù)名:

animals.sort { return $0 < $1 }

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

animals.sort { $0 < $1 }

后,由于Swift知道數(shù)組的元素符合equatable,因此可以簡單地編寫:

animals.sort(by: <)

問題四:

此代碼創(chuàng)建兩個類:Address 和 Person 。然后它創(chuàng)建兩個 Person 實(shí)例來表示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)

假設(shè)布 Brian 搬到街對面的新大樓,更新他的地址:

brian.address.fullAddress = "148 Tutorial Street"

它編譯,運(yùn)行時不會出錯。如果你現(xiàn)在查一下 Ray 的地址,他也搬到了新大樓。

print (ray.address.fullAddress)

這是怎么回事?你如何解決這個問題?

回答:

Address 是一個類,是引用類型,不管是通過Ray還是Brian訪問,都是相同的實(shí)例。更改 headquarters 地址將同時更改兩個人的地址。你能想象如果 Brian 收到Ray的郵件會發(fā)生什么情況嗎,反之亦然?

解決方案是創(chuàng)建一個新 Address 對象來分配給Brian,或者將 Address 聲明為結(jié)構(gòu)體。

口述問題

什么是可選的,可選可以解決哪些問題?

使用可選類型(optionals)來處理值可能缺失的情況。在objective-c中,只有在使用nil特殊值的引用類型中才可以表示值缺失。值類型(如int或float)不具有此功能。
Swift將缺乏值概念擴(kuò)展到引用類型和值類型。可選變量可以包含值或零,表示是否缺少值。

總結(jié)結(jié)構(gòu)體和類之間的主要區(qū)別。

差異總結(jié)為:

類支持繼承;結(jié)構(gòu)不支持。
類是引用類型;結(jié)構(gòu)體是值類型。
什么是通用類型,它們解決了什么問題?

在swift中,可以在函數(shù)和數(shù)據(jù)類型中使用泛型,例如在類、結(jié)構(gòu)體或枚舉中。
泛型解決了代碼重復(fù)的問題。當(dāng)有一個方法接受一種類型的參數(shù)時,通常會復(fù)制它以適應(yīng)不同類型的參數(shù)。
例如,在下面的代碼中,第二個函數(shù)是第一個函數(shù)的“克隆”,但它接受字符串而不是整數(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

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

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

areTheyEqual("ray", "ray")
areTheyEqual(1, 1)

由于在本例中測試的是相等性,所以將參數(shù)限制為遵守 Equatable 協(xié)議的任何類型。此代碼實(shí)現(xiàn)了預(yù)期的結(jié)果,并防止傳遞不同類型的參數(shù)。

在某些情況下,你無法避免使用隱式展開的選項(xiàng)。什么時候?為什么?

使用隱式展開選項(xiàng)的最常見原因是:

如果在實(shí)例化時無法初始化非本質(zhì)的屬性。
一個典型的例子是Interface Builder出口,它總是在它的所有者之后初始化。在這種特定情況下 - 假設(shè)它在Interface Builder中正確配置 - 你在使用它之前保證outlet是非零的。
解決強(qiáng)引用循環(huán)問題,即兩個實(shí)例相互引用并且需要對另一個實(shí)例的非空引用。在這種情況下,將一側(cè)標(biāo)記為無主引用,而另一側(cè)使用隱式解包可選。
打開可選項(xiàng)的各種方法有哪些?他們?nèi)绾卧u價安全性?

var x : String? = "Test"

提示:有七種方法。

強(qiáng)行打開 - 不安全。

let a: String = x!

隱式解包變量聲明 - 在許多情況下不安全。

var a = x!

可選綁定 - 安全。

if let a = x {
  print("x was successfully unwrapped and is = \(a)")
}

可選鏈接 - 安全。

let a = x?.count

無合并操作員 - 安全。

let a = x ?? ""

警衛(wèi)聲明 - 安全。

guard let a = x else {
  return
}

可選模式 - 安全。

if case let a? = x {
  print(a)
}

中級面試題

問題一:

nil 和 .none有什么區(qū)別?

沒有區(qū)別,因?yàn)镺ptional.none(簡稱.none)和nil是等價的。
實(shí)際上,下面的等式輸出為真:

nil == .none
使用nil更常見,推薦使用。

這是 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)

使用可變函數(shù)正確聲明ThermometerStruct以更改其內(nèi)部變量temperature。編譯器報錯是因?yàn)槟阍谕ㄟ^ let 創(chuàng)建的實(shí)例上調(diào)用了registerTemperature,因?yàn)樗遣豢勺兊?。將let改為var通過編譯。
對于結(jié)構(gòu)體,必須將內(nèi)部狀態(tài)更改為mutating的方法標(biāo)記,但不能從不可變變量中調(diào)用它們。

這段代碼會打印什么?為什么?

var thing = "cars"

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

thing = "airplanes"

closure()

它會打?。篒 love cars。聲明閉包時,捕獲列表會創(chuàng)建一個 thing 副本。這意味著即使為 thing 分配新值,捕獲的值也不會改變。

如果省略閉包中的捕獲列表,則編譯器使用引用而不是副本。因此,當(dāng)調(diào)用閉包時,它會反映對變量的任何更改??梢栽谝韵麓a中看到:

var thing = "cars"

let closure = {    
  print("I love \(thing)")
}

thing = "airplanes"

closure() // Prints: "I love airplanes"

問題四

這是一個全局函數(shù),用于計(jì)算數(shù)組中唯一值的數(shù)量:

func countUniques<T: Comparable>(_ array: Array<T>) -> Int {
  let sorted = array.sorted()
  let initial: (T?, Int) = (.none, 0)
  let reduced = sorted.reduce(initial) {
    ($1, $0.0 == $1 ? $0.1 : $0.1 + 1)
  }
  return reduced.1
}

它使用sorted方法,因此它將T限制為符合Comparable協(xié)議的類型。
你這樣調(diào)用它:

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

問題: 將此函數(shù)重寫為Array上的擴(kuò)展方法,以便你可以編寫如下內(nèi)容:

[1, 2, 3, 3].countUniques() // should print 3
你可以將全局countUniques(_ :)重寫為Array擴(kuò)展:

extension Array where Element: Comparable {
  func countUniques() -> Int {
    let sortedValues = sorted()
    let initial: (Element?, Int) = (.none, 0)
    let reduced = sortedValues.reduce(initial) { 
      ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) 
    }
    return reduced.1
  }
}

請注意,僅當(dāng)泛型 Element 符合Comparable時,新方法才可用。

問題五

這是 divide 對象的兩個可選 Double 類型變量的方法。在執(zhí)行實(shí)際解析之前,有三個先決條件需要驗(yàn)證:

變量 dividend 必須包含非空的值
變量 divisor 必須包含非空的值
變量 divisor 不能等于零

func divide(_ dividend: Double?, by divisor: Double?) -> Double? {
  if dividend == nil {
    return nil
  }
  if divisor == nil {
    return nil
  }
  if divisor == 0 {
    return nil
  }
  return dividend! / divisor!
}

使用guard語句并且不使用強(qiáng)制解包來改進(jìn)此功能。

func divide(_ dividend: Double?, by divisor: Double?) -> Double? {
    guard let dividend = dividend, let divisor = divisor, divisor != 0  else {
       return nil
    }
       return dividend / divisor
    }

問題六

使用 if let 重寫第五題

func divide(_ dividend: Double?, by divisor: Double?) -> Double? {
   if let dividend = dividend, let divisor = divisor, divisor != 0 {
      return dividend / divisor
   } else {
     return nil
   }
}

中級口述問題

第一題

在Objective-C中,聲明一個這樣的常量:

const int number = 0;

Swift對應(yīng)的寫法:

let number = 0

const是在編譯時使用值或表達(dá)式初始化的變量,必須在編譯時解析。
使用let創(chuàng)建的不可變是在運(yùn)行時確定的常量。你可以使用靜態(tài)或動態(tài)表達(dá)式對其進(jìn)行初始化。這允許聲明如下:

let higherNumber = number + 5

請注意,只能分配一次值。

第二題

聲明一個 static 修飾的屬性或函數(shù),請?jiān)谥殿愋蜕鲜褂胹tatic修飾符。這是一個結(jié)構(gòu)的例子:你用 static 修飾值類型。這是一個結(jié)構(gòu)體的例子:

struct Sun {
  static func illuminate() {}
}

對于類,可以使用static或class修飾符。他們實(shí)現(xiàn)了相同的目標(biāo),但方式不同。你能解釋一下它們有何不同?

static 使屬性或方法為靜態(tài)并不可重寫。使用class可以重寫屬性或方法。
應(yīng)用于類時,static將成為class final的別名。
例如,在此代碼中,當(dāng)你嘗試重寫 illuminate() 時,編譯器會抱錯:

class Star {
  class func spin() {}
  static func illuminate() {}
}
class Sun : Star {
  override class func spin() {
    super.spin()
  }
  // error: class method overrides a 'final' class method
  override static func illuminate() { 
    super.illuminate()
  }
}

問題三

你可以在 extension 里添加存儲屬性嗎?為什么行或不行呢?

不,這是不可能的。你可以使用擴(kuò)展來向現(xiàn)有類型添加新行為,但不能更改類型本身或其接口。如果添加存儲的屬性,則需要額外的內(nèi)存來存儲新值。擴(kuò)展程序無法管理此類任務(wù)。

問題四

Swift中的協(xié)議是什么?

協(xié)議是一種定義方法,屬性和其他要求藍(lán)圖的類型。然后,類,結(jié)構(gòu)或枚舉可以遵守協(xié)議來實(shí)現(xiàn)這些要求。
遵守協(xié)議需要實(shí)現(xiàn)該協(xié)議的要求。該協(xié)議本身不實(shí)現(xiàn)任何功能,而是定義功能??梢詳U(kuò)展協(xié)議以提供某些要求的默認(rèn)實(shí)現(xiàn)或符合類型可以利用的其他功能。

高級書面問題

第一題

思考以下 thermometer 模型結(jié)構(gòu)體:

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

可以使用以下代碼創(chuàng)建實(shí)例:

var t: Thermometer = Thermometer(temperature:56.8)

但以這種方式初始化它會更好:

var thermometer: Thermometer = 56.8

你可以嗎? 要怎么做?

Swift定義了一些協(xié)議,使你可以使用賦值運(yùn)算符初始化具有文字值的類型。

采用相應(yīng)的協(xié)議并提供公共初始化器允許特定類型的文字初始化。在 Thermometer 的情況下,實(shí)現(xiàn)ExpressibleByFloatLiteral如下:

extension Thermometer: ExpressibleByFloatLiteral {
  public init(floatLiteral value: FloatLiteralType) {
    self.init(temperature: value)
  }
}

現(xiàn)在,可以使用float創(chuàng)建實(shí)例。

var thermometer: Thermometer = 56.8

轉(zhuǎn)自:http://www.itdecent.cn/p/744e5c3af37e

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容