問題一:
下面代碼中變量 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 類型的新實例。使用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 實例來表示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訪問蜘拉,都是相同的實例。更改 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ù),同時保持類型安全油坝。下面是通用實現(xiàn):
func areTheyEqual<T: Equatable>(_ x: T, _ y: T) -> Bool {
return x == y
}
areTheyEqual("ray", "ray")
areTheyEqual(1, 1)
由于在本例中測試的是相等性嫉戚,所以將參數(shù)限制為遵守 Equatable 協(xié)議的任何類型刨裆。此代碼實現(xiàn)了預(yù)期的結(jié)果,并防止傳遞不同類型的參數(shù)彬檀。
在某些情況下帆啃,你無法避免使用隱式展開的選項。什么時候窍帝?為什么努潘?
使用隱式展開選項的最常見原因是:
如果在實例化時無法初始化非本質(zhì)的屬性。
一個典型的例子是Interface Builder出口坤学,它總是在它的所有者之后初始化疯坤。在這種特定情況下 - 假設(shè)它在Interface Builder中正確配置 - 你在使用它之前保證outlet是非零的。
解決強(qiáng)引用循環(huán)問題深浮,即兩個實例相互引用并且需要對另一個實例的非空引用压怠。在這種情況下,將一側(cè)標(biāo)記為無主引用飞苇,而另一側(cè)使用隱式解包可選菌瘫。
打開可選項的各種方法有哪些?他們?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ū)別匾浪,因為Optional.none(簡稱.none)和nil是等價的皇帮。
實際上,下面的等式輸出為真:
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逞频。編譯器報錯是因為你在通過 let 創(chuàng)建的實例上調(diào)用了registerTemperature纯衍,因為它是不可變的。將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()
它會打印:I love cars陷揪。聲明閉包時惋鸥,捕獲列表會創(chuàng)建一個 thing 副本。這意味著即使為 thing 分配新值悍缠,捕獲的值也不會改變卦绣。
如果省略閉包中的捕獲列表,則編譯器使用引用而不是副本扮休。因此迎卤,當(dāng)調(diào)用閉包時,它會反映對變量的任何更改玷坠∥仙Γ可以在以下代碼中看到:
var thing = "cars"
let closure = {
print("I love \(thing)")
}
thing = "airplanes"
closure() // Prints: "I love airplanes"
問題四
這是一個全局函數(shù),用于計算數(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í)行實際解析之前叔壤,有三個先決條件需要驗證:
變量 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ù)脚曾,請在值類型上使用static修飾符。這是一個結(jié)構(gòu)的例子:你用 static 修飾值類型启具。這是一個結(jié)構(gòu)體的例子:
struct Sun {
static func illuminate() {}
}
對于類本讥,可以使用static或class修飾符。他們實現(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é)議來實現(xiàn)這些要求须误。
遵守協(xié)議需要實現(xiàn)該協(xié)議的要求挨稿。該協(xié)議本身不實現(xiàn)任何功能,而是定義功能京痢∧谈剩可以擴(kuò)展協(xié)議以提供某些要求的默認(rèn)實現(xiàn)或符合類型可以利用的其他功能。
高級書面問題
第一題
思考以下 thermometer 模型結(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
你可以嗎? 要怎么做?
Swift定義了一些協(xié)議历造,使你可以使用賦值運(yùn)算符初始化具有文字值的類型甩十。
采用相應(yīng)的協(xié)議并提供公共初始化器允許特定類型的文字初始化。在 Thermometer 的情況下吭产,實現(xiàn)ExpressibleByFloatLiteral如下:
extension Thermometer: ExpressibleByFloatLiteral {
public init(floatLiteral value: FloatLiteralType) {
self.init(temperature: value)
}
}
現(xiàn)在侣监,可以使用float創(chuàng)建實例。
var thermometer: Thermometer = 56.8
轉(zhuǎn)自:http://www.reibang.com/p/744e5c3af37e