DevStore首页 > 文章 >文章详情

Swift面试题及答案(二)

fuli1024 | 2015-08-29 11:13    浏览量(805)    评论(0)   推荐(0)

Verbal questions 口头的提问

到达这一步你已经很优秀了,但是你还不能自称为绝地武士(意思是还不能认为自己很厉害了)任何人只要够努力都可以解决上面那些代码,但是如何处理下面这些不断出现的理论知识和实践问题呢?

回答这些问题仍然需要你在Playground里实际操作

一、初级

Question #1 - Swift 1.0 or later

什么是可选数据类型?它解决了什么问题?

答案:

可选数据类型是用来表示一个数据类型缺少具体值。在Objective-C中,只有引用类型才允许没有具体值,用的是nil来表示。比如float数据类型没有这样的特性。

Swift用可选数据类型扩展了值类型和引用数据类型。可选数据类型任何时候都允许拥有具体值或者为nil

Question #2 - Swift 1.0 or later

什么时候使用结构体,我们什么时候又应该使用类呢?

答案:

有一个正在进行的讨论,关于过度使用类超过结构体这究竟是好还是坏。

函数式编程倾向于多使用值数据类型,而面向对象编程更倾向于使用类。

在Swift中,类和结构体有很多不同的特性。可以得出下面这样一份总结:

·类支持继承,结构体不行

·类是引用类型,结构体是值类型

我们并没有一个规则来判定使用哪一个是最好。一般的建议是在能够达到你目标的前提下且使用到的代价最小(结构体比类节省内存空间)。除非你需要用到继承或者是引用的语法,否则那就采用结构体。

注意:在运行时,结构体比类具有更高性能的因为结构体的方法调用是静态绑定的,而类的方法调用是在运行时动态解析。这是另一个很好的理由来使用结构体,而不是使用类。

Question #3 - Swift 1.0 or later

什么是泛型,它们又解决了什么问题?

答案:

·泛型是用来使代码能安全工作。在Swift中,泛型可以在函数数据类型和普通数据类型中使用,例如类、结构体或枚举。

·泛型解决了代码复用的问题。有一种常见的情况,你有一个方法,需要一个类型的参数,你为了适应另一种类型的参数还得重新再写一遍这个方法。

比如,在下面的代码中,第二个方法是第一个方法的“克隆体”:

func areIntEqual(x: Int, _ y: Int) -> Bool {  return x == y
}func areStringsEqual(x: String, _ y: String) -> Bool {  return x == y
}
areStringsEqual("ray", "ray") // trueareIntEqual(1, 1) // true

一个Objective-C开发者可能会采用NSObject来解决问题:

import Foundationfunc areTheyEqual(x: NSObject, _ y: NSObject) -> Bool {  return x == y
}
areTheyEqual("ray", "ray") // trueareTheyEqual(1, 1) // true

这段代码能达到了目的,但是编译的时候并不安全。它允许一个字符串和一个整型数据进行比较:

areTheyEqual(1, "ray")

程序可能不会崩溃,但是允许一个字符串和一个整型数据进行比较可能不会得到想要的结果。

采用泛型的话,你可以将上面两个方法合并为一个,并同时还保证了数据类型安全。这是实现代码:

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

Question #4 - Swift 1.0 or later

偶尔你也会不可避免地使用隐式可选类型。那请问什么时候我们需要这么做?为什么需要这么做?

答案:

最常见的情况是:

1.不为nil的才能初始化它的值。一个典型的例子是一个界面生成器的出口(Interface Builder outlet),它总是在它的本体初始化后初始化。在这种情况下,如果它在界面构建器(Interface Builder)中正确地配置了,就能够保证在使用前outlet不为nil的。

2.为解决强引用循环问题(不知道循环引用是什么的可以看我们翻译的Swift官方文档自动引用计数篇)。当2个实例互相引用时,就需要一个不为nil的引用指向另一个实例。引用的一边可以修饰为unowned,另一边使用隐式可选类型,便可解决循环引用问题。

为方便大家理解我贴段代码上来(原文是没用的)

class Customer {    let name: String
    var card: CreditCard?    init(name: String) {        self.name = name
    }    deinit { print("\(name) is being deinitialized") }
}class CreditCard {    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {        self.number = number        self.customer = customer
    }    deinit { print("Card #\(number) is being deinitialized") }
}

理解得不太清楚可以点开上面链接查看

小贴士:尽量不要使用隐式可选类型。使用它们会增加运行时崩溃的几率。在某些情况下,出现崩溃也许是程序员需要这么做,这里也有一个更好的方法来达到同样的效果,例如,使用fatalerror()。

Question #5 - Swift 1.0 or later

你知道有哪些解包的方式?它们是否是安全解包的?

答案:

ps:下面代码为译者本人为方便读者阅读而添加,如还有不理解的地方可以根据关键字搜索相关文档

·强制用!展开 -- 操作不安全

·声明隐式可选类型变量 -- 在许多情况下是不安全的

(var implicitlyUnwrappedString: String!)

·optional binding -- 安全

var count: Int?count = 100if let validCount = count {  "count is " + String(validCount)    //count is 100} else {  "nil"}

·新的Swift2 guard声明 -- 安全

·自判断链接--安全

if let roomCount = john.residence?.numberOfRooms {  println("John's residence has \(roomCount) room(s).")
} else {  println("Unable to retrieve the number of rooms.")
}

·nil -- 安全

二、中级

渐渐地你挑战了这里。也许你之前问题解决得很好,但是让我们看看你是否能很好地通过下面这些问题。

Question #1 - Swift 1.0 or later

Swift是一种面向对象的语言还是一种面向函数的语言?

答案:

Swift是一种混合语言,同时支持这两种范式。

它实现了三种面向对象的基本原则

·封装

·继承

·多态

当Swift作为一种面向函数的语言来理解时,会有不同却相似的方式来定义它。

其中一种是较为常见的维基百科上的:"…a programming paradigm [...] that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data."

“……编程范式[……],将数学函数作为一种值,不需要考虑其中的状态变化和数据变化。”

你要觉得Swift是一个门成熟的面向函数的编程语言是比较牵强的,但它确实也具有很多面向函数编程的基本要素。

Question #2 - Swift 1.0 or later

下列哪些特性是Swift含有的?

·泛型类

·泛型结构

·泛型接口

答案:

Swift中包括了上述的1和2。泛型可以在类,结构体,枚举全局方法或者普通方法中使用。

3用typealias实现了。它本身不是一个泛型类型,它是一个占位符名称。它通常被称为关联类型,当一个协议声明时使用。如果有不明白的可查看喵神的文章

Question #3 - Swift 1.0 or later

在Objective-C语言中,常量可以被定义如下:

const int number = 0;

这是Swift对应的代码:

let number = 0

请问它们有什么区别么?如果有,你能解释下它们之间的区别么?

答案:

const 是一个变量在编译时初始化的值或着是在编译时解决初始化的。

let声明的常数是在运行的时候创建,它最终可以被初始化为静态或者动态的表达式。注意它的值只能被分配一次。

Question #4 - Swift 1.0 or later

Swift声明一个静态属性或静态函数可以使用static来修饰。这是一个结构体的例子:

struct Sun {  static func illuminate() {}
}

对于类,可以使用static或class来修饰。他们可以达到同样的目标,但实际上他们是不同的。你能解释他们有什么不同吗?

答案:

·使用static声明的一个静态属性或者方法并不可被覆盖override(子类覆盖父类的方法)。

·使用class就可以覆盖。

当用在类里的时候,static相当于class final

比如在下面这段代码中你如果覆盖illuminate()编译器就会报错

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

Question #5 - Swift 1.0 or later

可以使用扩展添加存储属性吗?

答案:

no,这是不可能的。扩展可以为已经存在的数据类型添加新的行为,但是不允许改变类型本身或它的接口。

如果您添加了存储的属性,您需要额外的内存来存储新的值。扩展不能完成这样的任务。

三、高级

Question #1 - Swift 1.2 or later

在Swift1.2中,你可以解释一下声明一个枚举类型的泛型的问题么?

以一个含有两个泛型参数T和V的枚举Either为例,用T作为Left的相关值类型,V作为Right的相关值类型:

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

小贴士:检查这种情况应该在一个Xcode中的项目中,不是在Playground上。还得注意,这个问题是Swift 1.2相关的,所以你需要Xcode 6.4。

答案:

编译失败的错误消息:

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

出现的问题是,不能确定T需要的内存大小。分配内存大小时取决于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>)
}

这个问题只在Swift 1.0或者以上出现,但是2.0已经解决了。

Question #2 - Swift 1.0 or later

闭包是值类型还是引用类型的?

答案:

闭包是引用类型。如果一个闭包被分配给一个变量,该变量被复制到另一个变量,它们实际是引用的相同一个闭包并且它里面的参数列表也同样会被复制。

Question #3 - Swift 1.0 or later

UInt数据类型用于存储整数。它实现了从一个带符号的整数转化为UInt的初始化方式:

init(_ value: Int)

然而,如果您提供了一个负值,例如下面的代码会产生一个编译错误:

let myNegative = UInt(-1)

我们知道负数在内部是使用补码表示,你怎么才能把一个负数的Int转化为UInt,同时保持它在内存中的表示形式?

答案:

这儿有一个初始化的方式:

UInt(bitPattern: Int)

Question #4 - Swift 1.0 or later

你能描述一下你用Swift时遇到的循环引用么?你是怎么解决的?

答案:

循环引用是指两个实例彼此强引用,导致内存泄漏,因为两个实例都不会被收回。原因是只要有一个强引用,实例就不会被回收。你可以通过 weak 或者 unowned 引用 替换其中一个强引用,从而打破强引用循环。

Question #5 - Swift 2.0 or later

Swift2.0增添了一个新关键字实现递归枚举。这里有一个枚举包含一个Node,Node有两个的相关的值类型,T和List:

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

请问那个可以实现递归枚举的关键字是什么?

答案:

indirect

代码如下:

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

恭喜你看到了文章末尾,如果你还是不太清楚那些答案,希望你也不要感到沮丧!其中的一些问题很复杂,Swift是一个非常丰富、且富有表现力的语言。我们还有很多需要学。此外,苹果还在不断改善Swift与添加新的功能,所以可能还存在一些非常好用的但是我们并不知道的功能(言外之意就是让我们多研究)。

最后,学习语言最好的方式是使用一门语言。Swift几乎可以与objective-c无缝地混合,在Playground上写写或在一个真正的项目中使用Swift,建立一个你已经非常熟悉的现有项目是一个很好的学习Swift的方法。

相关推荐:Swift面试题及答案(一)

本文为译文,原文:Swift Interview Questions and Answers

原文作者:Antonio Bello;译者:lfb_CD

  •   赞(0) 赞 +1 赞(0) 已赞
  •   收藏(0) 收藏 +1 已收藏 取消
  •   推荐上头条 推荐 +1 推荐上头条 已推荐
评论(0)

热门文章

暂时没有热门文章噢~