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中的结构体是值类型。是按值类型而不是引用类型复制值的。

创建了一个tutorial1的副本,并将其分配给tutorial2:

var tutorial2 = tutorial1

“ tutorial1 ”中未反映对“ tutorial12”的更改

如果 Tutorial 是一个类,那么 tutorial1 和 tutorial2 都等于 2.
swift中的类是引用类型。当你更改Tutorial1的属性时,你将看到它反映在Tutorial2中,反之亦然。

问题二:

你用var声明了view1,用let声明了view2。有什么区别,最后一行会编译通过吗?

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!
问题三:

这段复杂的代码按字母顺序对名称数组进行排序。尽可能简化闭包。

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

类型推断系统会自动判断闭包中参数的类型和返回类型,就可以去掉类型:

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

可以用$I符号替换参数名:

animals.sort { return $0 < $1 }

在单语句闭包中,可以省略返回关键字。最后一条语句的值将成为闭包的返回值:

animals.sort { $0 < $1 }

后,由于Swift知道数组的元素符合equatable,因此可以简单地编写:

animals.sort(by: <)
问题四:

此代码创建两个类:Address 和 Person 。然后它创建两个 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)

假设布 Brian 搬到街对面的新大楼,更新他的地址:

brian.address.fullAddress = "148 Tutorial Street"

它编译,运行时不会出错。如果你现在查一下 Ray 的地址,他也搬到了新大楼。

print (ray.address.fullAddress)

这是怎么回事?你如何解决这个问题?

回答:

Address 是一个类,是引用类型,不管是通过Ray还是Brian访问,都是相同的实例。更改 headquarters 地址将同时更改两个人的地址。你能想象如果 Brian 收到Ray的邮件会发生什么情况吗,反之亦然?

解决方案是创建一个新 Address 对象来分配给Brian,或者将 Address 声明为结构体。

口述问题

什么是可选的,可选可以解决哪些问题?

使用可选类型(optionals)来处理值可能缺失的情况。在objective-c中,只有在使用nil特殊值的引用类型中才可以表示值缺失。值类型(如int或float)不具有此功能。
Swift将缺乏值概念扩展到引用类型和值类型。可选变量可以包含值或零,表示是否缺少值。

总结结构体和类之间的主要区别。

差异总结为:

  • 类支持继承;结构不支持。
  • 类是引用类型;结构体是值类型。
什么是通用类型,它们解决了什么问题?

在swift中,可以在函数和数据类型中使用泛型,例如在类、结构体或枚举中。
泛型解决了代码重复的问题。当有一个方法接受一种类型的参数时,通常会复制它以适应不同类型的参数。
例如,在下面的代码中,第二个函数是第一个函数的“克隆”,但它接受字符串而不是整数。

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

通过采用泛型,可以将这两个函数合并为一个函数,同时保持类型安全。下面是通用实现:

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

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

由于在本例中测试的是相等性,所以将参数限制为遵守 Equatable 协议的任何类型。此代码实现了预期的结果,并防止传递不同类型的参数。

在某些情况下,你无法避免使用隐式展开的选项。什么时候?为什么?

使用隐式展开选项的最常见原因是:

  • 如果在实例化时无法初始化非本质的属性。
    一个典型的例子是Interface Builder出口,它总是在它的所有者之后初始化。在这种特定情况下 - 假设它在Interface Builder中正确配置 - 你在使用它之前保证outlet是非零的。
  • 解决强引用循环问题,即两个实例相互引用并且需要对另一个实例的非空引用。在这种情况下,将一侧标记为无主引用,而另一侧使用隐式解包可选。
打开可选项的各种方法有哪些?他们如何评价安全性?
var x : String? = "Test"

提示:有七种方法。

  • 强行打开 - 不安全。
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 ?? ""
  • 警卫声明 - 安全。
guard let a = x else {
  return
}
  • 可选模式 - 安全。
if case let a? = x {
  print(a)
}

中级面试题

问题一:

nil 和 .none有什么区别?

没有区别,因为Optional.none(简称.none)和nil是等价的。
实际上,下面的等式输出为真:

nil == .none

使用nil更常见,推荐使用。

这是 thermometer 作为类和结构的模型。编译器在最后一行报错。为什么编译失败?

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)

使用可变函数正确声明ThermometerStruct以更改其内部变量temperature。编译器报错是因为你在通过 let 创建的实例上调用了registerTemperature,因为它是不可变的。将let改为var通过编译。
对于结构体,必须将内部状态更改为mutating的方法标记,但不能从不可变变量中调用它们。

这段代码会打印什么?为什么?
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"
问题四

这是一个全局函数,用于计算数组中唯一值的数量:

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协议的类型。
你这样调用它:

countUniques([1, 2, 3, 3]) // result is 3
问题: 将此函数重写为Array上的扩展方法,以便你可以编写如下内容:
[1, 2, 3, 3].countUniques() // should print 3

你可以将全局countUniques(_ :)重写为Array扩展:

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

请注意,仅当泛型 Element 符合Comparable时,新方法才可用。

问题五

这是 divide 对象的两个可选 Double 类型变量的方法。在执行实际解析之前,有三个先决条件需要验证:

  • 变量 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语句并且不使用强制解包来改进此功能。
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对应的写法:

let number = 0

const是在编译时使用值或表达式初始化的变量,必须在编译时解析。
使用let创建的不可变是在运行时确定的常量。你可以使用静态或动态表达式对其进行初始化。这允许声明如下:

let higherNumber = number + 5

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

第二题

声明一个 static 修饰的属性或函数,请在值类型上使用static修饰符。这是一个结构的例子:你用 static 修饰值类型。这是一个结构体的例子:

struct Sun {
  static func illuminate() {}
}

对于类,可以使用static或class修饰符。他们实现了相同的目标,但方式不同。你能解释一下它们有何不同?

static 使属性或方法为静态并不可重写。使用class可以重写属性或方法。
应用于类时,static将成为class final的别名。
例如,在此代码中,当你尝试重写 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 里添加存储属性吗?为什么行或不行呢?

不,这是不可能的。你可以使用扩展来向现有类型添加新行为,但不能更改类型本身或其接口。如果添加存储的属性,则需要额外的内存来存储新值。扩展程序无法管理此类任务。

问题四

Swift中的协议是什么?

协议是一种定义方法,属性和其他要求蓝图的类型。然后,类,结构或枚举可以遵守协议来实现这些要求。
遵守协议需要实现该协议的要求。该协议本身不实现任何功能,而是定义功能。可以扩展协议以提供某些要求的默认实现或符合类型可以利用的其他功能。

高级书面问题

第一题

思考以下 thermometer 模型结构体:

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

可以使用以下代码创建实例:

var t: Thermometer = Thermometer(temperature:56.8)

但以这种方式初始化它会更好:

var thermometer: Thermometer = 56.8

你可以吗? 要怎么做?

Swift定义了一些协议,使你可以使用赋值运算符初始化具有文字值的类型。

采用相应的协议并提供公共初始化器允许特定类型的文字初始化。在 Thermometer 的情况下,实现ExpressibleByFloatLiteral如下:

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

现在,可以使用float创建实例。

var thermometer: Thermometer = 56.8
未完待续...
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,727评论 2 9
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,771评论 1 10
  • Hello Word 在屏幕上打印“Hello, world”,可以用一行代码实现: 你不需要为了输入输出或者字符...
    restkuan阅读 3,147评论 0 5
  • 一直没有时间好好看一下swift,最近复习了一遍语法,这里记录swift学习过程中遇到的一些问题和要点,和Obje...
    bomo阅读 2,332评论 0 25