iOS swift 面试题
来源:互联网 发布:python sin函数 编辑:程序博客网 时间:2024/06/17 01:34
Swift语言至今诞生有一年多的时间了,已经成为当前最流行语言之一。虽然它的语法简单好用,但实际上Swift是一门非常复杂的语言。因为它不仅是面向对象的同时又是函数式编程语言。本文主要介绍Swift常见的一些面试问题,你可以用这些问题向面试者提问,也可以用来测试你自己目前所掌握的Swift知识,如果你不清楚问题答案的话也不用太担心,因为每个问题下面都有相应的答案。
问题主要分为两个部分,笔试题和口头问题。
笔试题:可以通过发送Email方式进行编程测试,问题可以包含一小段代码测试。
口头问题:可以通过手机等方式进行面对面的交流,因为这些问题更适合用语言进行交流。
而且每个部分又分为三个等级:
初级:适用于刚接触Swift的学习者,已经读过一本或者两本Swift相关书籍,开始准备在App中使用Swift语言。
中级:适用于对Swift语言概念非常感兴趣的学习者,已经读过许多Swift博客文章并且想进一步深入学习Swift语言。
高级:适用于高级学者,已经对Swift语言很熟悉,喜欢探寻语言,想进一步挑战自我,喜欢高级技术者。
如果你想知道这些问题的答案,建议你最好打开Playground,亲自实现编码。下面这些问题的答案都在Xcode 7.0 beta 6上测试过。
笔试题
初级
你好,现在开始基础测试。
问题 #1 - Swift1.0或者更高版本
用下面方法写一个for循环是最好的方法吗?
for var i = 0; i < 5; i++ { print("Hello!")}
答案:
for _ in 0...4 { print("Hello!")}
Swift实现了两种范围操作符,分别为闭区间操作符和半开区间操作符。前者包括范围内的所有元素。例如下面例子包含了0到4所有元素。
0...4
而半开区间操作符不会包括最后一个元素。下面的例子同样包括了0到4所有元素
0..<5
问题 #2 - Swift1.0或者更高版本
考虑以下这段代码
struct Tutorial { var difficulty: Int = 1}var tutorial1 = Tutorial()var tutorial2 = tutorial1tutorial2.difficulty = 2
tutorial1.difficulty和tutorial2.difficulty的值有什么不同,如果Tutorial是一个类,又有什么不同?为什么?
答案:
tutorial1.difficulty的值是1,而tutorial2.difficulty的值是2
在Swift中,结构体是值类型而不是引用类型,它是值copy。
下面这行代码会首先创建一份tutorial1的copy,然后再赋值给tutorial2
var tutorial2 = tutorial1
从这行代码可以看出,tutorial2 的变化不会影响tutorial1
如果Tutorial是一个类,tutorial1.difficulty和tutorial2.difficulty的值都是2。
在Swift中,类是引用类型,tutorial1的变化会影响到tutorial2,反之亦然。
问题 #3 - Swift1.0或者更高版本
用var声明view1和用let声明view2,在下面的例子中有什么不同?,最后一行Code能否编译通过?
import UIKitvar view1 = UIView()view1.alpha = 0.5let view2 = UIView()view2.alpha = 0.5 // Will this line compile?
答案:
因为view1是一个变量,所以可以用UIView的实例进行赋值,用let关键字只能赋值一次,所以下面代码不能编译成功。
view2 = view1 // 错误:view2是不可变的
但是,UIView是基于类引用的,所以view2的属性是可以改变的(最后一行代码可以编译通过)
let view2 = UIView()view2.alpha = 0.5 // Yes!
问题 #4 - Swift1.0或者更高版本
下面Code是把数组按字母顺序进行排序,看起来有些复杂,你能用闭包简化它吗?
let animals = ["fish", "cat", "chicken", "dog"]let sortedAnimals = animals.sort { (one: String, two: String) -> Bool in return one < two}
答案:
首先是可以简化闭包的参数,因为在闭包中,系统是可以通过类型推断方式推算出参数的类型。所以你可以去掉参数的类型:
let sortedAnimals = animals.sort { (one, two) -> Bool in return one < two }
返回类型也可以推算出来,所以可以去掉闭包的返回类型:
let sortedAnimals = animals.sort { (one, two) in return one < two }
可以用$i符号替换掉参数的名字,代码然后就变成这样:
let sortedAnimals = animals.sort { return $0 < $1 }
在单语句的闭包中,关键字return也可以省略。最后一条语句的返回值就变成闭包的返回值:
let sortedAnimals = animals.sort { $0 < $1 }
Oops, 到目前,是不是非常简单了,但实际上并非如此。
对字符串来说,有一个字符串比较函数,定义如下:
func <(lhs: String, rhs: String) -> Bool
使用这个函数可以让你的Code更加简洁, 如下:
let sortedAnimals = animals.sort(<)
注意:以上每一步的代码编译运行后都会输出同样的结果,你可以选择使用单字节的闭包。
问题 #5 - Swift1.0或者更高版本
下面代码定义Address和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也会更新地址。因为类Address是引用类型,headquarters是同一个实例,不论你是修改ray的地址还是brian的地址,都会改变headquarters地址。
解决方法是为brian新创建一个地址。或者声明Address为结构体而不是类。
中级
下面提升一下难度的等级
问题 #1 - Swift2.0或者更高版本
思考一下以下代码:
var optional1: String? = nilvar optional2: String? = .None
nil和.None有什么不同?变量optional1和optional2有什么不同?
答案:
nil和.None是一样的。当可选变量没有值时,Optional.None(.None for short)是一般初始化可选变量的方式,而nil则是另一种写法。
事实上,下面条件语句输出是true:
nil == .None // On Swift 1.x this doesn't compile. You need Optional<Int>.None
记着下面代码说明enumeration是一个可选类型:
enum Optional<T> { case None case Some(T)}
问题 #2 - Swift1.0或者更高版本
下面是用类和结构体实现温度计的例子:
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)
这段代码在哪个地方会出现编译错误?为什么?
小提示:在Playground测试之前,请仔细阅读并思考代码。
答案:
在最后一行编译器会提示错误,结构体ThermometerStruct声明一个可变的函数修改内部变量 temperature,但是registerTemperature却被一个用let创建的实例所调用,用let定义的变量是不可变的,所以编译通不过。
在结构体中,改变内部状态的方法必须用mutating声明,而且不允许用不可变的实例调用它。
问题 #3 - Swift1.0或者更高版本
下面的代码打印输出是什么?为什么?
var thing = "cars"let closure = { [thing] in print("I love \(thing)")}thing = "airplanes"closure()
答案:
结果会打印出“I love cars”。当声明闭包的时候,捕获列表会创建一份thing的copy,所以被捕获到的值是不会改变的,即使你改变thing的值。
如果你去掉闭包中的捕获列表,编译器会使用引用代替copy。在这种情况下,当闭包被调用时,变量的值是可以改变的。示例如下:
var thing = "cars"let closure = { print("I love \(thing)")}thing = "airplanes"closure() // Prints "I love airplanes"
问题 #4 - Swift2.0或者更高版本
这是一个全局函数,用来记录数组中唯一值的数量。
func countUniques<T: Comparable>(array: Array<T>) -> Int { let sorted = array.sort(<) let initial: (T?, Int) = (.None, 0) let reduced = sorted.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) } return reduced.1}
它使用<和==操作符,所以T类型需要遵循Comparable协议。
可以像下面这样调用它:
countUniques([1, 2, 3, 3]) // result is 3
重写这个函数,让它作为数组Array的方法,使得可以这样调用:
[1, 2, 3, 3].countUniques() // should print 3
答案:
在Swift2.0中,泛型是可以被扩展的,但需要类型约束限制,如果泛型不满足约束,那么扩展也是不可见或者不可访问的。
因此,全局函数countUniques可以被重写为数组Array的扩展:
extension Array where Element: Comparable { func countUniques() -> Int { let sorted = sort(<) let initial: (Element?, Int) = (.None, 0) let reduced = sorted.reduce(initial) { ($1, $0.0 == $1 ? $0.1 : $0.1 + 1) } return reduced.1 }}
注意这个被重写的方法只有当泛型的类型Element实现了Comparable协议才是有效的。例如,当你用装满UIView的数组调用这个方法时编译器会提示错误:
import UIKitlet a = [UIView(), UIView()]a.countUniques() // compiler error here because UIView doesn't implement Comparable
问题 #5 - Swift2.0或者更高版本
下面是一个计算两个可选类型除法的函数。在计算之前满足以下三个条件:
- 被除数不能为空(not nil)
- 除数不能为空(not nil)
- 除数不能为0
这段代码能够按照预期的那样进行工作,但同时存在两个问题:func divide(dividend: Double?, by divisor: Double?) -> Double? { if dividend == .None { return .None } if divisor == .None { return .None } if divisor == 0 { return .None } return dividend! / divisor!}
- 前置条件,可以考虑利用guard关键字
- 使用了强制解包
改进这个函数,使用guard条件并且避免使用强制解包.
答案:在Swift2.0中引用guard语句关键字,如果不满足时,guard会提供一个退出路径。这个关键字在判断预置条件时非常有用,能更加清晰表达条件,不需要采用金字塔似的多重嵌套if语句,下面是一个例子:
guard dividend != .None else { return .None }
它还可以用在可选绑定上,能够访问解包后的变量:
guard let dividend = dividend else { return .None }
因此重写后的divide函数如下:
func divide(dividend: Double?, by divisor: Double?) -> Double? { guard let dividend = dividend else { return .None } guard let divisor = divisor else { return .None } guard divisor != 0 else { return .None } return dividend / divisor}
注意在最后一行没有隐士解包操作符,是因为dividend和divisor已经被解包存储,并且是非可选不可变的。
而且可以成组地监视guard条件,可以让函数变得更加简单:func divide(dividend: Double?, by divisor: Double?) -> Double? { guard let dividend = dividend, divisor = divisor where divisor != 0 else { return .None } return dividend / divisor}
现在只有两个guard条件,因为使用where语句判断解包后的变量divisor是否为0。
高级
问题 #1 - Swift1.0或者更高版本
思考以下用结构体定义的温度计:
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定义了以下协议,通过使用赋值操作符,用字面值直接初始化:
- NilLiteralConvertible
- BooleanLiteralConvertible
- IntegerLiteralConvertible
- FloatLiteralConvertible
- UnicodeScalarLiteralConvertible
- ExtendedGraphemeClusterLiteralConvertible
- StringLiteralConvertible
- ArrayLiteralConvertible
- DictionaryLiteralConvertible
采用相对应的协议,提供一个公有的构造器,允许字面值方式初始化它。在这个温度计这个例子中,需要实现FloatLiteralConvertible协议:现在可以使用float值创建一个实例了:
extension Thermometer : FloatLiteralConvertible {public init(floatLiteral value: FloatLiteralType) { self.init(temperature: value)}}
var thermometer: Thermometer = 56.8
问题 #2 - Swift1.0或者更高版本
Swift中定义了运算操作符和逻辑操作符,用于操作不同的类型。当然,你可以自定义一些运算操作符,比如一元或者二元的。
定义一个^^幂操作符,并且同时满足以下要求:
- 用两个Int型整数作为参数。
- 返回幂函数的值,第一个参数为底数,第二个参数为指数。
- 实现潜在的溢出错误。
答案:
创建一个自定义运算符需要两步:声明和实现。
声明使用关键字operator,用于指定类型(一元或者二元),然后指定运算符的结合性和优先级。
在本例中,运算符是^^,类型是infix,结合性是右结合,优先级设置为155,鉴于乘法和除法的优先级是150。以下是运算符的声明:
infix operator ^^ { associativity right precedence 155 }
实现如下:
func ^^(lhs: Int, rhs: Int) -> Int { let l = Double(lhs) let r = Double(rhs) let p = pow(l, r) return Int(p)}
注意一点,实现没有考虑溢出的情况,如果运算结果超过Int.max,将会产生运行时错误。
问题 #3 - Swift1.0或者更高版本
你能像下面这样用原始值定义枚举类型吗?为什么?
enum Edges : (Double, Double) { case TopLeft = (0.0, 0.0) case TopRight = (1.0, 0.0) case BottomLeft = (0.0, 1.0) case BottomRight = (1.0, 1.0)}
答案:
不能,原始值的类型必须遵循以下条件:
- 实现Equatable协议
- 必须是以下文字转换的类型:
- Int
- String
- Character
在以上代码中,原始数值类型是元组类型,即使元组中的数值满足条件,也是不兼容的。
问题 #4 - Swift2.0或者更高版本
思考以下代码,定义了结构体Pizza,协议Pizzeria,在协议扩展中实现默认方法makeMargherita()。
struct Pizza { let ingredients: [String]}protocol Pizzeria { func makePizza(ingredients: [String]) -> Pizza func makeMargherita() -> Pizza}extension Pizzeria { func makeMargherita() -> Pizza { return makePizza(["tomato", "mozzarella"]) }}
然后定义一个餐馆Lombardis:
struct Lombardis: Pizzeria { func makePizza(ingredients: [String]) -> Pizza { return Pizza(ingredients: ingredients) } func makeMargherita() -> Pizza { return makePizza(["tomato", "basil", "mozzarella"]) }}
下面代码创建了Lombardis的两个实例。哪一个会使用“basil”来做披萨?
let lombardis1: Pizzeria = Lombardis()let lombardis2: Lombardis = Lombardis()lombardis1.makeMargherita()lombardis2.makeMargherita()
答案:
两个都会。协议Pizzeria声明了makeMargherita()方法,并且提供了一个默认的实现。在Lombardis实现中,这个方法被重写了。因为这个方法在协议中声明了,在Runtime时能正确的被调用。
如果协议中没有声明makeMargherita()方法,但是在协议的扩展中又默认实现了 这个方法会怎样?
protocol Pizzeria { func makePizza(ingredients: [String]) -> Pizza}extension Pizzeria { func makeMargherita() -> Pizza { return makePizza(["tomato", "mozzarella"]) }}
在这种情况下,只有lombardis2能用“basil”来做披萨,而lombardis1则不会,因为它调用的是扩展中默认的方法。
问题 #5 - Swift2.0或者更高版本
下面代码会有编译错误,你能指出在哪个地方吗?为什么?
struct Kitten {}func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") } print(k)}
提示:有三种方式可以fix它
答案:
在guard中else语句体中需要有退出路径,用return返回,或者抛出一个异常或者调用@noreturn。最简单的方式是return语句:
func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") return } print(k)}
下面版本是抛出异常:
enum KittenError: ErrorType { case NoKitten}struct Kitten {}func showKitten(kitten: Kitten?) throws { guard let k = kitten else { print("There is no kitten") throw KittenError.NoKitten } print(k)}try showKitten(nil)
最后一个方法是调用@noreturn中的一个函数fatalError():
struct Kitten {}func showKitten(kitten: Kitten?) { guard let k = kitten else { print("There is no kitten") fatalError() } print(k)}
口头答疑
初级
问题 #1 - Swift1.0或者更高版本
可选是什么?可以解决什么问题?
答案:
可选可以使得任何类型的变量都能表达缺省值。在Objective-C中,缺省值只适用于引用类型,通常被指定为nil。对于基础类型的变量(例如int, float)则没有这个功能,
而Swift把缺省值概念扩展到引用类型和值类型中。一个可选变量在任何时候可以有值或者为nil。
问题 #2 - Swift1.0或者更高版本
什么时候使用结构体?什么时候使用类?
答案:
目前关于使用类还是结构体这个问题,有许多的争论。在函数式编程倾向于使用值类型,而面向对象编程中更喜欢用类。
在Swift中,类和结构体有很多不相同的功能,主要有下面几点:
- 类支持继承,而结构体不支持。
- 类是引用类型,结构体是值类型
并没有统一的规则决定孰好孰坏。通常推荐使用最适合的工具完成特定的目标,在swift中比较好的做法是使用结构体,除非你需要用到继承或者引用的时候才使用类。
主要因为在运行时,结构体的性能优于类的,结构体的方法调用是静态绑定的,而类是在Runtime时动态解析的。
问题 #3 - Swift1.0或者更高版本
泛型是什么?用来解决什么问题?
答案:
泛型可以使某一个类型的算法能够更安全的工作。在Swift中泛型可以用在函数和数据类型上,如类,结构体和枚举类型。
泛型还能解决代码重复的问题。普遍现象是当你已经有一个带参数的方法,但你又不得不再重新写一遍有着类似类型的方法。
在下面的例子中,第二个函数就像是第一个函数的“clone”,它只是把传入参数的类型从字符串变为整型。
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
虽然这种方式是能解决问题,但是在编译期间是不安全的。因为它会允许比较String类型和Integer类型,就像下面这样:
areTheyEqual(1, "ray")
虽然应用程序不会crash,但是允许字符串和整型进行比较,会出现让你想不到的结果。
使用泛型的话,可以把两个函数合成一个,同时又能保证类型是安全的。下面是一个具体实现:
func areTheyEqual<T: Equatable>(x: T, _ y: T) -> Bool { return x == y}areTheyEqual("ray", "ray")areTheyEqual(1, 1)
这个例子是测试两个数是否相等,你可以限制传入参数类型是任意类型,只要这个类型实现了Equatable协议。这段代码可以得到你想要的结果并且能防止传入不同参数类型。
问题 #4 - Swift1.0或者更高版本
在某些情况下,你不得不使用隐式解包可选?什么场合?为什么?
答案:
下面情况下需要使用隐式解包可选:
- 当属性在初始化阶段不能为空的时候。一个典型的例子是IB的输出口,它总是要初始化的。使用它之前已经在IB中配置,outlet确保在使用之前值不为空。
- 解决强引用循环的问题,当两个实例相互引用,需要一个实例是非空引用。在这种情况下,一个实例可以标注unowned,另一个使用隐式解包可选。
提示:不要使用隐式解包可选,除非你必须使用时。如果使用它不当时,会增加Runtime时crash的机率。
问题 #5 - Swift1.0或者更高版本
解包可选类型方法是什么?它们的安全性怎样?
提示:有7种方法
答案:
- forced unwrapping ! operator -- unsafe
- implicitly unwrapped variable declaration -- unsafe in many cases
- optional binding -- safe
- optional chaining -- safe
- nil coalescing operator -- safe
- new Swift 2.0 guard statement -- safe
- new Swift 2.0 optional pattern -- safe
中级
问题 #1 - Swift1.0或者更高版本
Swift是面向对象语言还是函数式语言?
答案:
Swift是混合式语言,两种都支持。
它实现面向对象的三个基本特征:
- 封装
- 继承
- 多态
和函数式语言相比,Swift有一些不同,虽然它满足函数式语言基本要求,但并不是完全成熟的函数式编程语言。
问题 #2 - Swift1.0或者更高版本
下面功能,哪一个在Swift中有?
- 泛型类
- 泛型数据结构
- 泛型协议
答案:
1和2在Swift中有。泛型可以用在类,结构体,枚举类型,全局函数和方法中。
3可以通过关键字typealias部分实现。它并不是泛型,只是占位符名字而已。它常被看作是关联数据类型,采用协议时才会被定义。
问题 #3 - Swift1.0或者更高版本
在Objective-C中,一个常量可以像下面进行声明:
const int number = 0;
下面是Swift中的:
let number = 0
他们之间有什么不同吗?如果是的,你能解释一下吗?
答案:
const是一个变量在编译期间被初始化值或者在编译期间表达式的值。
通过let关键字创建常量是在Runtime时初始化的,它能够用用静态的或者动态表达式的结果初始化。注意它的值只能被初始化一次。
问题 #4 - Swift1.0或者更高版本
声明一个静态属性或者函数,你可以使用关键字static来修饰值类型。以下是一个结构体的例子:
struct Sun { static func illuminate() {}}
对于类来说,你可以使用static或者class修饰符。虽然它们完成同样的功能,但实际上是不同的。你能解释一下它们之间有什么不同吗?
答案:
使用static关键字,静态属性和静态函数是不能被重写的,但当使用class关键字,你可以重写属性和函数。
其实,对于类来说,static关键字是class final的别名而已。
例如,你编译下面这些code时,当你要重写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() }}
问题 #5 - Swift1.0或者更高版本
使用extension可以增加存储属性吗?解释一下
答案:
是不能的。extension是用来给存在的类型添加新行为的,并不能改变类型或者接口本身。如果你增加存储属性,你需要额外的内存空间存储新的值。extension是不能管理这样的任务的。
高级
问题 #1 - Swift1.2
在Swift1.2中,你能解释一下用泛型声明枚举类型的问题吗?以两个泛型T和V的枚举类型Either为例,Left为关联T类型,Right关联V类型。
enum Either<T, V> { case Left(T) case Right(V)}
提示:检验这个问题要用Xcode工程,不要在Playground中。注意这个问题和Swift1.2有关,你需要使用Xcode6.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>)}
这个问题只会影响Swift1.0或之后版本,在Swift2.0中已经解决。
问题 #2 - Swift1.0或者更高版本
闭包是值类型还是引用类型?
答案:
闭包是引用类型。如果一个闭包赋值给一个变量,这个变量又复制一份copy给另一个变量,那么变量所引用的闭包和捕获的列表也会copy一份。
问题 #3 - Swift1.0或者更高版本
UInt类型用来存储无符号整数。它实现如下一个用有符号的整数构造器:
init(_ value: Int)
但是如果你提供一个负整数的话,下面代码会产生编译错误。
let myNegative = UInt(-1)
知道计算机负数是用二进制补码作为一个正数进行表示,你怎样把Int类型的负整数转为UInt数?
答案:
已经有一个初始化器可以完成:
UInt(bitPattern: Int)
问题 #4 - Swift1.0或者更高版本
你能描述一下在Swift中哪些地方会产生循环引用?有什么解决办法吗?
当两个实例之间相互进行强引用的时候,就会引起循环引用。两个实例都不会释放内存,就会造成内存泄露。可用weak或者unowned打破实例之间的强引用问题,这样两个实例才有机会释放内存空间。
问题 #5 - Swift1.0或者更高版本
Swift2.0引用了一个新关键字能产生递归枚举类型。下面是一个带有Node节点的枚举类型,Node关联值类型,T和list:
enum List<T> { case Node(T, List<T>)}
那个关键字是什么?
答案:
关键字indirect允许递归枚举类型,像下面这样:
enum List<T> { indirect case Cons(T, List<T>)}
结束语
所有的Swfit资源都来自于官方文档《The Swift Programming Language》,学习一门语言最好的方法是使用它。所以可以在Playground使用它或者在实际项目中运用它,Swift能够和Objective-C进行无缝对接。本文是翻译Ray的博客。
作者:TedX
链接:http://www.jianshu.com/p/a78d1df6c042
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
- iOS swift 面试题
- Swift面试题
- Swift面试题
- Swift 面试题
- 【面试必备】iOS-Swift 面试题及其答案
- iOS常见的算法面试题及(swift)答案
- 我遇到过的 iOS 面试题(swift)
- Swift 面试题及其答案
- Swift 面试题及其答案
- iOS技术面试题
- ios面试题
- IOS面试题
- ios基础面试题
- IOS 面试题
- iOS开发面试题
- iOS 面试题二
- iOS面试题
- IOS 面试题
- HDU
- php设计模式
- 动态规划之01背包
- 51nod 1237 杜教筛
- ZXing 二维码扫描
- iOS swift 面试题
- JAVA基础知识点
- JSTL 学习使用笔记
- mongodb释放内存
- 阿里云ECS之完整流程搭建:CentOS 7.3+Nginx 1.12.1+php 7.1 + MaraiaDB 5.5.52 + PhpMyAdmin 4.6.6
- Mac安装配置Tomcat
- 大数据基本概念学习
- 机器学习-交叉验证
- Java并发编程:volatile关键字解析