問題一:
下面代碼中變量 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