文章目录
  1. 1. Question1: Extending Protocols With Default Implementation with Enums crashes Xcode
    1. 1.1. 问题描述
    2. 1.2. 问题解答
  2. 2. Question2: What does _: mean in Swift?
    1. 2.1. 问题描述
    2. 2.2. 问题解答
  3. 3. Question3:swift difference between final var and non-final var | final let and non-final let
    1. 3.1. 问题解答
  4. 4. Question4: Swift array of generics
    1. 4.1. 问题描述
  5. 5. Question5: Parentheses in Function and Closure
    1. 5.1. 问题描述
    2. 5.2. 问题解答

作者:shanks

本周共整理了 5 个问题。涉及问题有:扩展协议问题,函数表示问题,final关键字问题,泛型问题和含有闭包的函数定义问题。

本周整理问题如下:

对应的代码都放到了 github 上,有兴趣的同学可以下载下来研究:点击下载

Question1: Extending Protocols With Default Implementation with Enums crashes Xcode

点击打开问题原地址

问题描述

以下代码会在extension部分挂掉,playground 会弹出错误提示:Communication with the playground service was interrupted unexpectedly.楼主的本意是想给specialAbility提供一个默认值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum Ability {
case Flying
case Running
case Swimming
case Hiding
}
//All animals will conform to this
protocol Animal {
var name: String { get }
var specialAbility: Ability { get }
}
struct Dog: Animal {
var name: String
var specialAbility: Ability
}
let rex = Dog.init(name: "Rex", specialAbility: .Flying)
// 以下代码会crash
extension Animal where Self: Dog {
var specialAbility: Ability { return .Running }
}

问题解答

从 crash 的提示信息,看不出来是什么错,不知道苹果会不会在3.0修复这些提示不准确的问题。问题是由扩展Animal协议造成的,where语句的约束条件,不能是一个具体的类,只能是协议。而Dog是具体的类,where的知识点可以查看官方翻译教程:Where 子句
要解决默认值的问题,新增一个指定构造器即可:
)

1
2
3
4
5
6
7
8
9
extension Dog {
init(name: String) {
self.specialAbility = .Running
self.name = name
}
}
let rex1 = Dog.init(name: "Rex")
rex1.specialAbility

Question2: What does _: mean in Swift?

点击打开问题原地址

问题描述

楼主在学习 Swift 语言的过程中,阅读了一些学习资料,比如官方文档。看到一些函数定义写成了这样:recordResponse(_:)。不理解这样写表示什么意思。

问题解答

在官方文档中,使用这种方式表达函数是一种约定,可以从官方文档Functions章节中看到很多这样的表示。刚开始看文档时候,也不太适应这种表示方式。也找不到为什么要这样表示,省略了很多函数的细节(比如没有返回值),把问题发到翻译组内部讨论群里面后,大家都一致认为,这是表达函数的一种约定的方式,至于怎么用这种表达式,下面这段话基本概括了用法:

  • 简单的说,函数签名里每个冒号都代表一个参数,每个参数都要有个外部参数名写在冒号前面,写_代表不要外部参数名. 参数名都是自动生成,默认情况下第一个参数不生成外部参数名

大家在写博客时候,可以参照这种写法了,比如以下一些例子:

  • sayHello(_:):传入一个参数
  • print(_:separator:terminator:):传入3个参数

Question3:swift difference between final var and non-final var | final let and non-final let

点击打开问题原地址

楼主的问题是关于final关键字的,带有final修饰符的变量和没有带的变量区别是什么?

1
2
3
4
5
6
7
var someVar = 5
final var someFinalVar = 5
let someLet = 5
final let someFinalLet = 5

问题解答

final关键字主要用来修饰类和类中的属性、方法或者下标。子类继承时,不能重写父类中带有final修饰的属性、方法或者下标。如果final修饰的是类,那么此类不能被继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 没有final修饰属性的情况
class A {
var x: Int {return 5}
}
class B : A {
override var x: Int {return 3}
}
var b = B()
assert(b.x == 3)
// 有final修饰的情况,报错
class A {
final var x: Int {return 5}
}
class B : A {
// 此处报错
override var x: Int {return 3}
}

更多final的细节,可以看看喵神关于final单独一篇tips

Question4: Swift array of generics

点击打开问题原地址

问题描述

楼主定义了一个泛型结构体,声明2个变量后,想把这2个变量塞到一个数组里面去,数组定义为[Thing],编译报错。

1
2
3
4
5
6
7
8
struct Thing<T> {
}
let intThing = Thing<Int>()
let stringThing = Thing<String>()
// 编译错误:Cannot convert value of type 'Thing<Int>' to expected type 'Thing'
let things: [Thing] = [intThing, stringThing]

//: ### 问题解答

Thing是一个泛型结构体,不能直接使用[Thing]表明一个数组。只能通过万能的Any来符合编译器的规则。不过这样做,显然就不能直接使用Thing里面的方法了,得做一下转换:

1
2
3
let things: [Any] = [intThing, stringThing]
var intThing1 = things[0] as! Thing<Int>

如果想要直接[Thing]这样的语法,可以使用枚举, 而使用枚举,就不能使用泛型了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum Thing {
case Text(String)
case Integer(Int)
}
let thingWithText = Thing.Text("Hello world")
let thingWithInt = Thing.Integer(123)
let things = [thingWithText, thingWithInt]
things.forEach { (thing) -> () in
switch thing {
case .Text(let text): print(text)
case .Integer(let integer): print(integer)
}
}

翻译组翻译了一篇关于枚举使用的很详细的文章:Swift 中枚举高级用法及实践,值得一看!

Question5: Parentheses in Function and Closure

点击打开问题原地址

问题描述

有以下三种函数定义方式,有什么区别?

1
2
3
4
5
func myFunction() -> (()-> Int)
func myFunction1() -> (Void-> Int)
func myFunction2() -> Void-> Int

问题解答

  • () 和 Void 是一样的,2种不同的写法而已。看你喜欢,Swift 中比较常用 ()。
  • -> 操作符是右关联的,也就是说,多个 -> 连接在一起时候,编译器优先解析右边的。

所以以上3个定义是一样的。第一个和第二个虽然加了括号,优先处理最右边的,但是因为->是右关联的,所以加括号和不加括号是一样的。

这3个表达式表示的意思是:定义一个函数,没有传入参数,返回一个闭包, 闭包的类型是() -> Int

楼下还举了一个更复杂的例子加深理解:

1
2
3
4
5
6
7
8
9
10
11
func myFuncA() -> () -> Int -> String {
return { () -> Int -> String in return { (i: Int) -> String in String(i) } }
}
func myFuncB() -> () -> (Int -> String) {
return { () -> Int -> String in return { (i: Int) -> String in String(i) } }
}
func myFuncC() -> (() -> Int) -> String {
return { (f: () -> Int) in String(f()) }
}

这个例子中,myFuncAmyFuncB 是一样的。没有传入参数,返回一个闭包,这个闭包也没有传入参数,返回是另外一个闭包。而返回的闭包类型是 Int -> String
myFuncC 也是没有传入参数,返回一个闭包。这个闭包传入参数是一个闭包,返回是一个String。传入闭包类型是: () -> Int

文章目录
  1. 1. Question1: Extending Protocols With Default Implementation with Enums crashes Xcode
    1. 1.1. 问题描述
    2. 1.2. 问题解答
  2. 2. Question2: What does _: mean in Swift?
    1. 2.1. 问题描述
    2. 2.2. 问题解答
  3. 3. Question3:swift difference between final var and non-final var | final let and non-final let
    1. 3.1. 问题解答
  4. 4. Question4: Swift array of generics
    1. 4.1. 问题描述
  5. 5. Question5: Parentheses in Function and Closure
    1. 5.1. 问题描述
    2. 5.2. 问题解答